home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / MoreNetworkSetup / NetworkSetup / NetworkSetupHelpers.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  147.7 KB  |  4,754 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        NetworkSetupHelpers.c
  3.  
  4.     Contains:    High-level network preference routines.
  5.  
  6.     Written by:    Quinn
  7.  
  8.     Copyright:    Copyright © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.  
  20.         <21>     18/1/00    Quinn   Further updates for the new Network Setup header file.
  21.         <20>     17/1/00    Quinn   Updates for latest Network Setup headers.
  22.         <19>      6/1/00    Quinn   Updated NSHEncodeRemotePassword to use the Network Setup routine
  23.                                     OTCfgEncrypt if it's available (Mac OS 9.0 and higher).
  24.         <18>     7/12/99    Quinn   AOL registers as an Ethernet device, even though it supports
  25.                                     dialling.  They will fix this, but I've added a special case to
  26.                                     handle it here as well.
  27.         <17>    19/10/99    Quinn   Fix embarrassing spelling error.
  28.         <16>    12/10/99    Quinn   Fixed bug in the 'isdm' stage of
  29.                                     BuildPackedPrefsFromTCPv4ConfigurationDigest where, if no
  30.                                     fSearchDomains was specified, we would set up
  31.                                     kTCPRoutersListAttr instead of kTCPDomainsListAttr.  Thanks to
  32.                                     John Norstad.
  33.         <15>     13/9/99    Quinn   Fix bug where CreateConfigurationDatabase wasn't setting up
  34.                                     cookie4 correctly.  Thanks for Tom Bayley.
  35.         <14>     13/9/99    Quinn   Fix bug in creating MacIP configurations.  Thanks to Thomas
  36.                                     Weisbach for the fix.
  37.         <13>     13/9/99    Quinn   Implement DHCPRelease.
  38.         <12>     26/5/99    Quinn   Use cookie4 in NSHConfigurationEntry.  Implement support for
  39.                                     Internet setup routines on legacy files.  Improved code sharing
  40.                                     between database and legacy files. Fixed bug in
  41.                                     BuildPackedPrefsFromModemConfigurationDigest; duplicated 'mdpw'
  42.                                     prefs.
  43.         <11>      7/5/99    Quinn   This "redux" word, I don't think it means what you think it
  44.                                     means.
  45.         <10>     22/4/99    Quinn   Added code to encode Remote Access passwords.  Also reworked how
  46.                                     the default 'lcp ' preference was generated, using Network Setup
  47.                                     if possible.
  48.          <9>     21/4/99    Quinn   Added massive amounts of code to create, duplicate, get, set,
  49.                                     delete and rename configurations for TCP/IP, Remote Access, and
  50.                                     Modem.  Also fixed a bug in NSHIsAppleTalkActive where it was
  51.                                     return an assembly Boolean (0/0xff) rather than a C Boolean
  52.                                     (0/1).
  53.          <8>     16/3/99    Quinn   Fixed one-based array problem in GetConfigurationListFromFile.
  54.          <7>    10/11/98    Quinn   Fix = vs == in an assert.
  55.          <6>    10/11/98    Quinn   Convert "MorePrefix.h" to "MoreSetup.h".
  56.          <5>     9/11/98    Quinn   AppleTalk on/off support.
  57.          <4>     9/11/98    Quinn   Add "TCP will dial" code.
  58.          <3>     5/11/98    Quinn   Use MoreAssertQ instead of MoreAssert.
  59.          <2>     5/11/98    Quinn   Fix header.
  60.          <1>     5/11/98    Quinn   First checked in.
  61. */
  62.  
  63. /////////////////////////////////////////////////////////////////
  64.  
  65. // MoreIsBetter Setup
  66.  
  67. #include "MoreSetup.h"
  68.  
  69. // Mac OS Interfaces
  70.  
  71. #include <Types.h>
  72. #include <Files.h>
  73. #include <Errors.h>
  74. #include <Folders.h>
  75. #include <Resources.h>
  76. #include <Gestalt.h>
  77. #include <CodeFragments.h>
  78. #include <NetworkSetup.h>
  79. #include <OpenTptLinks.h>
  80. #include <OpenTptConfig.h>
  81. #include <Traps.h>
  82.  
  83. // MIB Prototypes (stuff that should be in Universal Interfaces)
  84.  
  85. #include "RemoteAccessInterface.h"
  86.  
  87. // MIB Prototypes (stuff that should be in Universal Interfaces)
  88.  
  89. #include "MoreTextUtils.h"
  90. #include "MoreInterfaceLib.h"
  91. #include "MoreNetworkSetup.h"
  92. #include "OldOTConfigLib.h"
  93.  
  94. // Our Prototypes
  95.  
  96. #include "NetworkSetupHelpers.h"
  97.  
  98. /////////////////////////////////////////////////////////////////
  99. // Testing Parameters
  100.  
  101. enum {
  102.  
  103.     // Throw this switch if you want to debug the direct preference
  104.     // file access code on a machine with Network Setup installed.
  105.  
  106.     kUseNetworkSetup = true,
  107.     
  108.     // If you set kUseInetInterfaceInfo to false, NSHTCPWillDial will not
  109.     // use the heuristic of "if the TCP/IP stack is loaded, it's safe
  110.     // to open an endpoint".  This is especially useful when debugging.
  111.  
  112.     kUseInetInterfaceInfo = true
  113.     
  114. };
  115.  
  116. /////////////////////////////////////////////////////////////////
  117.  
  118. extern pascal ItemCount NSHCountConfigurationList(NSHConfigurationListHandle configList)
  119.     // See comments in interface part.
  120. {
  121.     return GetHandleSize( (Handle) configList ) / sizeof(NSHConfigurationEntry);
  122. }
  123.  
  124. /////////////////////////////////////////////////////////////////
  125. #pragma mark ----- Configuration List using Database -----
  126.  
  127. // Parameter structure for SetterIterator.
  128.  
  129. struct TypeAndClassParam {
  130.     OSType  fType;
  131.     OSType  fClass;
  132.     Boolean found;
  133.     CfgEntityRef *currentEntity;
  134. };
  135. typedef struct TypeAndClassParam TypeAndClassParam;
  136.  
  137. static pascal void TypeAndClassIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p)
  138.     // This routine is used as a callback for MNSIterateSet.
  139.     // It looks for the entity specified by the fType and fClass
  140.     // fields of the param, and puts its entityRef into the
  141.     // variable pointed to by the currentEntity field of param.
  142. {
  143.     TypeAndClassParam *param;
  144.     
  145.     param = (TypeAndClassParam *) p;
  146.  
  147.     MoreAssertQ(MNSValidDatabase(ref));
  148.     MoreAssertQ(thisElement != nil);
  149.     MoreAssertQ(param != nil);
  150.     MoreAssertQ(param->currentEntity != nil);
  151.     
  152.     if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) {
  153.         MoreAssertQ( ! param->found );
  154.         param->found = true;
  155.         *(param->currentEntity) = thisElement->fEntityRef;
  156.     }
  157. }
  158.  
  159. enum {
  160.     kNoCurrentConnectionErr = -6
  161. };
  162.  
  163. static OSStatus FindCurrentConnection(const MNSDatabaseRef *ref, OSType protocol, CfgEntityRef *currentEntity)
  164.     // This routine finds the current connection entity for the specified
  165.     // protocol in the active set, returning it in currentEntity.
  166. {
  167.     OSStatus err;
  168.     CfgEntityRef activeSet;
  169.     TypeAndClassParam param;
  170.     
  171.     MoreAssertQ(MNSValidDatabase(ref));
  172.     MoreAssertQ(currentEntity != nil);
  173.     
  174.     param.fClass = kOTCfgClassNetworkConnection;
  175.     param.fType = protocol;
  176.     param.found = false;
  177.     param.currentEntity = currentEntity;
  178.     
  179.     err = MNSFindActiveSet(ref, &activeSet);
  180.     if (err == noErr) {
  181.         err = MNSIterateSet(ref, &activeSet, TypeAndClassIterator, ¶m, false);
  182.     }
  183.     if (err == noErr) {
  184.         if (param.found) {
  185.             // Set preferences contain entities from weird areas because
  186.             // of the way the database is committed.  We can safely fix
  187.             // that up here.  I discussed with the Network Setup engineer
  188.             // and he reassured me that this was cool -- Quinn, 9 Nov 1998
  189.             
  190.             currentEntity->fLoc = ref->area;
  191.         } else {
  192.             err = kNoCurrentConnectionErr;
  193.         }
  194.     }
  195.     
  196.     return err;
  197. }
  198.  
  199. static OSStatus AddEntityToConfigurationList(const MNSDatabaseRef *ref,
  200.                                             const CfgEntityRef *entity,
  201.                                             const CfgEntityInfo *entityInfo,
  202.                                             OSType protocol,
  203.                                             NSHConfigurationListHandle configList)
  204.     // This routines adds the entity specified by entity and entityInfo
  205.     // in the database specified by ref to the configList.
  206. {
  207.     OSStatus err;
  208.     NSHConfigurationEntry thisEntry;
  209.     StringPtr entityName;
  210.     ByteCount junkSize;
  211.  
  212.     MoreAssertQ(configList != nil);
  213.     MoreAssertQ(MNSValidDatabase(ref));
  214.     MoreAssertQ(entity != nil);
  215.     MoreAssertQ(entityInfo != nil);
  216.     
  217.     // Get the user-visible name from the configuration, which is
  218.     // stored in the 'pnam' preferences.
  219.     
  220.     entityName = nil;
  221.     err = MNSGetPref(ref, entity, kOTCfgUserVisibleNamePref, &entityName, &junkSize);
  222.     if (err == noErr) {
  223.         MoreAssertQ(junkSize == (entityName[0] + 1));
  224.         BlockMoveData(entityName, &thisEntry.name, entityName[0] + 1);
  225.         thisEntry.selected = false;
  226.         thisEntry.cookie = 0;
  227.         thisEntry.cookie2 = *entity;
  228.         thisEntry.cookie3 = *entityInfo;
  229.         thisEntry.cookie4 = protocol;
  230.     }
  231.     
  232.     if (err == noErr) {
  233.         err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry));
  234.     }
  235.     
  236.     // Clean up.
  237.     
  238.     if (entityName != nil) {
  239.         DisposePtr( (Ptr) entityName);
  240.         MoreAssertQ(MemError() == noErr);
  241.     }
  242.     
  243.     return err;
  244. }
  245.  
  246. // Parameter structure for SetterIterator.
  247.  
  248. struct SetterParam {
  249.     OSType fType;
  250.     OSType fClass;
  251.     const CfgEntityRef *chosenConfig;
  252.     const CfgEntityInfo *chosenConfigInfo;
  253. };
  254. typedef struct SetterParam SetterParam;
  255.  
  256. static pascal void SetterIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p)
  257.     // This routine is used as a callback for MNSIterateSet.
  258.     // It looks for the entity specified by the fType and fClass
  259.     // fields of the param, and replaces it with the chosen
  260.     // entity and info from the param.  It expects that the caller
  261.     // of MNSIterateSet has specified writeAfterIterate so that
  262.     // the changes get written back to the set.
  263. {
  264.     SetterParam *param;
  265.     
  266.     param = (SetterParam *) p;
  267.  
  268.     MoreAssertQ(MNSValidDatabase(ref));
  269.     MoreAssertQ(MNSDatabaseWritable(ref));
  270.     MoreAssertQ(thisElement != nil);
  271.     MoreAssertQ(param != nil);
  272.     MoreAssertQ(param->chosenConfig != nil);
  273.     MoreAssertQ(param->chosenConfigInfo != nil);
  274.     
  275.     if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) {
  276.         thisElement->fEntityRef = *param->chosenConfig;
  277.         thisElement->fEntityInfo = *param->chosenConfigInfo;
  278.     }
  279. }
  280.  
  281. static OSStatus GetConfigurationListFromDatabase(OSType protocol, NSHConfigurationListHandle configList)
  282.     // Implementation of NSHGetConfigurationList which uses the Network Setup
  283.     // database.  See NSHGetConfigurationList's comment in header
  284.     // file for interface specification.
  285. {
  286.     OSStatus err;
  287.     OSStatus err2;
  288.     MNSDatabaseRef ref;
  289.     ItemCount entityCount;
  290.     CfgEntityRef *entityRefs;
  291.     CfgEntityInfo *entityInfos;
  292.     CfgEntityRef activeConn;
  293.     ItemCount i;
  294.  
  295.     entityRefs = nil;
  296.     entityInfos = nil;
  297.  
  298.     err = MNSOpenDatabase(&ref, false);
  299.     if (err == noErr) {
  300.     
  301.         // Find all the network connection entities for this protocol.
  302.     
  303.         err = MNSGetEntitiesList(&ref,
  304.                                 kOTCfgClassNetworkConnection, protocol,
  305.                                 &entityCount,
  306.                                 &entityRefs,
  307.                                 &entityInfos);
  308.  
  309.         // Add each to the list of possible connections.
  310.         
  311.         if (err == noErr) {
  312.             for (i = 0; i < entityCount; i++) {
  313.                 err = AddEntityToConfigurationList(&ref, &entityRefs[i], &entityInfos[i], protocol, configList);
  314.             }
  315.         }
  316.         
  317.         // Find the current configuration and mark it as selected
  318.         // in the list.
  319.         
  320.         if (err == noErr) {
  321.             err = FindCurrentConnection(&ref, protocol, &activeConn);
  322.         }
  323.         if (err == noErr) {
  324.             for (i = 0; i < entityCount; i++) {
  325.                 if ( OTCfgIsSameEntityRef(&activeConn, &(*configList)[i].cookie2, kOTCfgIgnoreArea) ) {
  326.                     (*configList)[i].selected = true;
  327.                 }
  328.             }
  329.         }
  330.  
  331.         err2 = MNSCloseDatabase(&ref, false);
  332.         if (err == noErr) {
  333.             err = err2;
  334.         }
  335.     }
  336.     
  337.     // Clean up.
  338.     
  339.     if (entityInfos != nil) {
  340.         DisposePtr( (Ptr) entityInfos);
  341.         MoreAssertQ(MemError() == noErr);
  342.     }
  343.     if (entityRefs != nil) {
  344.         DisposePtr( (Ptr) entityRefs);
  345.         MoreAssertQ(MemError() == noErr);
  346.     }
  347.     return err;
  348. }
  349.  
  350. static OSStatus SelectConfigurationFromDatabase(const NSHConfigurationEntry *chosenEntry)
  351.     // Implementation of NSHSelectConfiguration which uses the Network Setup
  352.     // database.  See NSHSelectConfiguration's comment in header
  353.     // file for interface specification.
  354. {
  355.     OSStatus err;
  356.     OSStatus err2;
  357.     MNSDatabaseRef ref;
  358.     CfgEntityRef activeSet;
  359.     SetterParam param;
  360.  
  361.     err = MNSOpenDatabase(&ref, true);
  362.     if (err == noErr) {
  363.         param.fClass = kOTCfgClassNetworkConnection;
  364.         param.fType = chosenEntry->cookie4;
  365.         param.chosenConfig = &chosenEntry->cookie2;
  366.         param.chosenConfigInfo = &chosenEntry->cookie3;
  367.         
  368.         err = MNSFindActiveSet(&ref, &activeSet);
  369.         if (err == noErr) {
  370.             err = MNSIterateSet(&ref, &activeSet, SetterIterator, ¶m, true);
  371.         }
  372.  
  373.         err2 = MNSCloseDatabase(&ref, err == noErr);
  374.         if (err == noErr) {
  375.             err = err2;
  376.         }
  377.     }
  378.     return err;
  379. }
  380.  
  381. /////////////////////////////////////////////////////////////////
  382. #pragma mark ----- Configuration List using File -----
  383.  
  384. static OSStatus SearchFolder(SInt16 vRefNum, SInt32 dirID,
  385.                                 OSType typeToSearchFor, OSType creatorToSearchFor,
  386.                                 FSSpec *fss)
  387.     // Search a particular folder for a file of a particular
  388.     // type and creator.  If it's found, return an FSSpec to
  389.     // the file.  If it's not found, return an error.
  390. {
  391.     OSStatus err;
  392.     Boolean found;
  393.     SInt16 index;
  394.     HParamBlockRec pb;
  395.     
  396.     MoreAssertQ(fss != nil);
  397.     
  398.     fss->vRefNum = vRefNum;
  399.     fss->parID = dirID;
  400.     
  401.     found = false;
  402.     index = 1;
  403.     do {
  404.         pb.fileParam.ioVRefNum = vRefNum;
  405.         pb.fileParam.ioDirID = dirID;
  406.         pb.fileParam.ioNamePtr = fss->name;
  407.         pb.fileParam.ioFDirIndex = index;
  408.         err = PBHGetFInfoSync(&pb);
  409.         if (err == noErr) {
  410.             found = ( pb.fileParam.ioFlFndrInfo.fdType    == typeToSearchFor &&
  411.                       pb.fileParam.ioFlFndrInfo.fdCreator == creatorToSearchFor );
  412.         }
  413.         index += 1;
  414.     } while (err == noErr & ! found);
  415.  
  416.     return err;
  417. }
  418.  
  419. enum {
  420.     kOTNetworkPrefFileType = 'pref',
  421.     kOTTCPPrefFileCreator = 'ztcp',
  422.     kOTAppleTalkPrefFileCreator = 'atdv',
  423.     
  424.     kModemPrefFileType     = 'mdpf',
  425.     kModemPrefFileCreator  = 'modm',
  426.     
  427.     kRemotePrefFileType    = 'lzcn',
  428.     kRemotePrefFileCreator = 'rmot'
  429. };
  430.  
  431. static OSStatus FindNetworkPrefFile(OSType protocol, FSSpec *fss)
  432.     // This routine scans the Preferences folder looking
  433.     // for the preferences for the given network protocol.
  434.     // Scans are done by file type and creator to avoid
  435.     // problems on localised systems.
  436. {
  437.     OSStatus err;
  438.     Boolean searchSubFolders;
  439.     OSType typeToSearchFor;
  440.     OSType creatorToSearchFor;
  441.     SInt16 prefFolderVRefNum;
  442.     SInt32 prefFolderDirID;
  443.     
  444.     // Set up the file type and creator based on the protocol.
  445.     
  446.     searchSubFolders = false;
  447.     switch (protocol) {
  448.         case kOTCfgTypeTCPv4:
  449.             typeToSearchFor    = kOTNetworkPrefFileType;
  450.             creatorToSearchFor = kOTTCPPrefFileCreator;
  451.             break;
  452.         case kOTCfgTypeAppleTalk:
  453.             typeToSearchFor    = kOTNetworkPrefFileType;
  454.             creatorToSearchFor = kOTAppleTalkPrefFileCreator;
  455.             break;
  456.         case kOTCfgTypeRemote:
  457.             typeToSearchFor    = kRemotePrefFileType;
  458.             creatorToSearchFor = kRemotePrefFileCreator;
  459.             searchSubFolders = true;
  460.             break;
  461.         case kOTCfgTypeModem:
  462.             typeToSearchFor    = kModemPrefFileType;
  463.             creatorToSearchFor = kModemPrefFileCreator;
  464.             break;
  465.         default:
  466.             MoreAssertQ(false);
  467.             break;
  468.     }
  469.     
  470.     // Search the Preferences folder for a file with that type and creator.
  471.     
  472.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &prefFolderVRefNum, &prefFolderDirID);
  473.     if (err == noErr) {
  474.     
  475.         // The Remote Access preference file is stored in a folder within
  476.         // the preferences folder.  We can't hard wire the name "Remote Access"
  477.         // because the name is localised.  Instead, we search all the folders
  478.         // within the preferences folder for the file.  In all other cases,
  479.         // we just search the preferences folder for the file.
  480.         
  481.         if (searchSubFolders) {
  482.             Boolean found;
  483.             CInfoPBRec cpb;
  484.             SInt16 index;
  485.  
  486.             found = false;
  487.             index = 1;
  488.             do {
  489.                 cpb.dirInfo.ioVRefNum = prefFolderVRefNum;
  490.                 cpb.dirInfo.ioDrDirID = prefFolderDirID;
  491.                 cpb.dirInfo.ioNamePtr = nil;
  492.                 cpb.dirInfo.ioFDirIndex = index;
  493.                 err = PBGetCatInfoSync(&cpb);
  494.                 if (err == noErr && ((cpb.dirInfo.ioFlAttrib & ioDirMask) != 0)) {
  495.                     found = ( SearchFolder(prefFolderVRefNum, cpb.dirInfo.ioDrDirID,
  496.                                             typeToSearchFor, creatorToSearchFor,
  497.                                             fss) == noErr);
  498.                 }
  499.                 index += 1;
  500.             } while (err == noErr & ! found);
  501.         } else {
  502.             err = SearchFolder(prefFolderVRefNum, prefFolderDirID,
  503.                                 typeToSearchFor, creatorToSearchFor,
  504.                                 fss);
  505.         }
  506.     }
  507.     return err;
  508. }
  509.  
  510. static OSStatus CheckResError(void *testH)
  511.     // A trivial wrapper routine for ResError,
  512.     // which is too lame to report an error code
  513.     // in all cases when GetResource fails.
  514. {
  515.     OSStatus err;
  516.  
  517.     err = ResError();
  518.     if (err == noErr && testH == nil) {
  519.         err = resNotFound;
  520.     }
  521.     return err;
  522. }
  523.  
  524. static OSStatus OpenNetworkPrefFile(OSType protocol, SInt8 permission,
  525.                                     SInt16 *oldResFile, SInt16 *networkResFile)
  526.     // Opens the legacy preference file for the given protocol with
  527.     // the specified permission (typicallly fsRdPerm or fsRdWrPerm).
  528.     // Returns the previous CurResFile and the refnum of the new
  529.     // file, both of which you pass to CloseNetworkPrefFile to clean up
  530.     // the open.
  531. {
  532.     OSStatus err;
  533.     FSSpec fss;
  534.     
  535.     MoreAssertQ(oldResFile != nil);
  536.     MoreAssertQ(networkResFile != nil);
  537.     
  538.     *oldResFile = CurResFile();
  539.     err = FindNetworkPrefFile(protocol, &fss);
  540.     if (err == noErr) {
  541.         if (permission == fsRdWrPerm) {
  542.             // ••• Gotcha •••
  543.             // Really need to be careful here because it's possible
  544.             // that fss is open in our current resource chain.
  545.             // See DTS Technote 1120 "Opening Resource Files Twice Considered
  546.             // Hard?" for details.
  547.             //
  548.             //   <http://developer.apple.com/technotes/tn/tn1120.html>
  549.             //
  550.             // I'll probably put real code to detect and handle this into
  551.             // MoreResources eventually; in the mean time, you have to live 
  552.             // with the limitation that you can't use this library when
  553.             // a legacy preference file might be open in your resource chain.
  554.             // -- Quinn, 9 Nov 1998
  555.         }
  556.         
  557.         // SetResLoad to false around the open to avoid bringing
  558.         // any preload resources in the file into memory.
  559.         
  560.         SetResLoad(false);
  561.         *networkResFile = FSpOpenResFile(&fss, permission);
  562.         err = ResError();
  563.         SetResLoad(true);
  564.     }
  565.     
  566.     // If we error, setup the outputs to values we can use to
  567.     // detect client logic errors in CloseNetworkPrefFile.
  568.     
  569.     if (err != noErr) {
  570.         *oldResFile = 0;
  571.         *networkResFile = 0;
  572.     }
  573.     
  574.     return err;
  575. }
  576.  
  577. static OSStatus CloseNetworkPrefFile(SInt16 oldResFile, SInt16 networkResFile)
  578.     // Closes the legacy preference file you opened using OpenNetworkPrefFile.
  579. {
  580.     OSStatus err;
  581.     
  582.     MoreAssertQ(oldResFile != 0);
  583.     MoreAssertQ(networkResFile != 0);
  584.     
  585.     CloseResFile(networkResFile);
  586.     err = ResError();
  587.     if (err == noErr) {
  588.         UseResFile(oldResFile);
  589.         MoreAssertQ(ResError() == noErr);
  590.     }
  591.     return err;
  592. }
  593.  
  594. static OSStatus CommitChangesToPrefFile(OSType protocol, SInt16 refNum, SInt16 config)
  595.     // This routine represents the magic that allows you to force OT
  596.     // to notice a configuration file change without rebooting.  It uses
  597.     // some previously undocumented routines, the glue for which is provided
  598.     // as part of this sample.  This routine is called by any direct file
  599.     // modification code which modifies the active configuration.
  600.     //
  601.     // IMPORTANT:
  602.     // The only reason it's safe to document these routines now is that we
  603.     // know that they work on all old versions of OT, and new versions of OT
  604.     // include the Network Setup library which allows you to change network
  605.     // preferences without any of this hackery.  Never ship a product that
  606.     // relies on these routines that doesn't also use Network Setup if it's
  607.     // available.
  608. {
  609.     OSStatus err;
  610.     
  611.     err = noErr;
  612.     switch (protocol) {
  613.         case kOTCfgTypeTCPv4:
  614.             if ( TCPCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) {
  615.                 err = -7;
  616.             }
  617.             if ( err == noErr ) {
  618.                 err = TCPChangeConfiguration(refNum, config);
  619.             }
  620.             break;
  621.         case kOTCfgTypeAppleTalk:
  622.             if ( ATCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) {
  623.                 err = -7;
  624.             }
  625.             if ( err == noErr ) {
  626.                 err = ATChangeConfiguration(refNum, config);
  627.             }
  628.             break;
  629.         case kOTCfgTypeRemote:
  630.         case kOTCfgTypeModem:
  631.             {
  632.                 Handle currentConfigResourceH;
  633.  
  634.                 // For Remote and Modem, we directly munge the preferences
  635.                 // file.  There's no way to tweak these protocol stacks
  636.                 // to get them to recognise the updated file.  Instead,
  637.                 // they'll pick up the preferences the next time you connect.
  638.                 // Hmmm, except for ARA Personal Server, for which we
  639.                 // have no solution at the moment.
  640.                 
  641.                 currentConfigResourceH = Get1Resource(kOTCfgCompatSelectedPref, 1);
  642.                 err = CheckResError(currentConfigResourceH);
  643.  
  644.                 if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) {
  645.                     // Assert: 'ccfg' is of the wrong size
  646.                     MoreAssertQ(false);
  647.                     err = -1;
  648.                 } else {
  649.                     **(SInt16 **)currentConfigResourceH = config;
  650.                     ChangedResource(currentConfigResourceH);
  651.                     err = ResError();
  652.                 }
  653.             }
  654.             break;
  655.         default:
  656.             MoreAssertQ(false);
  657.             err = -7;
  658.             break;
  659.     }
  660.     return err;
  661. }
  662.  
  663. static OSStatus GetCurrentConfigFromPrefFile(SInt16 *config)
  664.     // This routine returns the resource ID of the current
  665.     // configurator in a legacy preferences file.
  666. {
  667.     OSStatus err;
  668.     Handle currentConfigResourceH;
  669.     
  670.     MoreAssertQ(config != nil);
  671.     
  672.     currentConfigResourceH = Get1Resource(kOTCfgCompatSelectedPref, 1);
  673.     err = CheckResError(currentConfigResourceH);
  674.  
  675.     if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) {
  676.         // Assert: 'ccfg' is of the wrong size
  677.         MoreAssertQ(false);
  678.         err = -1;
  679.     } else {
  680.         *config = **(SInt16 **)currentConfigResourceH;
  681.     }
  682.     return err;
  683. }
  684.  
  685. static OSStatus AddResourceToConfigurationList(OSType protocol, Handle cnamHandle, NSHConfigurationListHandle configList)
  686.     // Given a handle to a 'cnam' resource, generate a
  687.     // NSHConfigurationEntry and append it to the list
  688.     // of configurations.
  689. {
  690.     OSStatus err;
  691.     NSHConfigurationEntry thisEntry;
  692.     SInt16 cnamID;
  693.     ResType junkType;
  694.     
  695.     GetResInfo(cnamHandle, &cnamID, &junkType, thisEntry.name);
  696.     MoreAssertQ(ResError() == noErr);
  697.     MoreAssertQ(junkType == kOTCfgCompatNamePref);
  698.     thisEntry.selected = false;
  699.     thisEntry.cookie = cnamID;
  700.     OTMemzero(&thisEntry.cookie2, sizeof(thisEntry.cookie2));
  701.     OTMemzero(&thisEntry.cookie3, sizeof(thisEntry.cookie3));
  702.     thisEntry.cookie4 = protocol;
  703.     
  704.     err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry));
  705.     return err;
  706. }
  707.  
  708. static OSStatus GetConfigurationListFromFile(OSType protocol, NSHConfigurationListHandle configList)
  709.     // Implementation of NSHGetConfigurationList which uses the legacy
  710.     // preference files.  See NSHGetConfigurationList's comment in header
  711.     // file for interface specification.
  712. {
  713.     OSStatus err;
  714.     OSStatus err2;
  715.     Handle cnamHandle;
  716.     SInt16 refNum;
  717.     SInt16 resCount;
  718.     SInt16 i;
  719.     SInt16 currentConfigID;
  720.     SInt16 oldResFile;
  721.     
  722.     err = OpenNetworkPrefFile(protocol, fsRdPerm, &oldResFile, &refNum);
  723.     if (err == noErr) {
  724.         resCount = Count1Resources(kOTCfgCompatNamePref);
  725.  
  726.         for (i = 1; i <= resCount; i++) {
  727.             SetResLoad(false);
  728.             cnamHandle = Get1IndResource(kOTCfgCompatNamePref, i);
  729.             err = CheckResError(cnamHandle);
  730.             SetResLoad(true);
  731.             
  732.             if (err == noErr) {
  733.                 err = AddResourceToConfigurationList(protocol, cnamHandle, configList);
  734.             }
  735.             
  736.             // Don't need to release the resource because CloseResFile will
  737.             // clean it up.
  738.             
  739.             if (err != noErr) {
  740.                 break;
  741.             }
  742.         }
  743.         
  744.         if (err == noErr) {
  745.             err = GetCurrentConfigFromPrefFile(¤tConfigID);
  746.         }
  747.         if (err == noErr) {
  748.             for (i = 0; i < resCount; i++) {
  749.                 if ( (*configList)[i].cookie == currentConfigID ) {
  750.                     (*configList)[i].selected = true;
  751.                 }
  752.             }
  753.         }
  754.         
  755.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  756.         if (err == noErr) {
  757.             err = err2;
  758.         }
  759.     }
  760.  
  761.     return err;
  762. }
  763.  
  764. static OSStatus SelectConfigurationFromFile(const NSHConfigurationEntry *chosenEntry)
  765.     // Implementation of NSHGetConfigurationList which uses the legacy
  766.     // preference files.  See NSHGetConfigurationList's comment in header
  767.     // file for interface specification.
  768. {
  769.     OSStatus err;
  770.     OSStatus err2;
  771.     SInt16 refNum;
  772.     SInt16 oldResFile;
  773.  
  774.     err = OpenNetworkPrefFile(chosenEntry->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  775.     if (err == noErr) {
  776.         err = CommitChangesToPrefFile(chosenEntry->cookie4, refNum, chosenEntry->cookie);
  777.         
  778.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  779.         if (err == noErr) {
  780.             err = err2;
  781.         }
  782.     }
  783.  
  784.     return err;
  785. }
  786.  
  787. /////////////////////////////////////////////////////////////////
  788. #pragma mark ----- Configuration List Abstraction ------
  789.  
  790. extern pascal OSStatus  NSHGetConfigurationList(OSType protocol, NSHConfigurationListHandle configList)
  791.     // See comments in interface part.
  792. {
  793.     OSStatus err;
  794.     
  795.     SetHandleSize( (Handle) configList, 0);
  796.     MoreAssertQ(MemError() == noErr);
  797.     
  798.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  799.         #if TARGET_RT_MAC_CFM
  800.             err = GetConfigurationListFromDatabase(protocol, configList);
  801.         #else
  802.             // Network Setup has no Mixed Mode glue.  When running
  803.             // code on a PowerPC with Network Setup available, you
  804.             // should either compile your code as Fat or, if that's
  805.             // infeasible, write your own Mixed Mode glue.
  806.             return -5;
  807.         #endif
  808.     } else {
  809.         err = GetConfigurationListFromFile(protocol, configList);
  810.     }
  811.     return err;
  812. }
  813.  
  814. extern pascal OSStatus  NSHSelectConfiguration(const NSHConfigurationEntry *chosenEntry)
  815.     // See comments in interface part.
  816. {
  817.     OSStatus err;
  818.     
  819.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  820.         #if TARGET_RT_MAC_CFM
  821.             err = SelectConfigurationFromDatabase(chosenEntry);
  822.         #else
  823.             // Network Setup has no Mixed Mode glue.  When running
  824.             // code on a PowerPC with Network Setup available, you
  825.             // should either compile your code as Fat or, if that's
  826.             // infeasible, write your own Mixed Mode glue.
  827.             return -5;
  828.         #endif
  829.     } else {
  830.         err = SelectConfigurationFromFile(chosenEntry);
  831.     }
  832.     return err;
  833. }
  834.  
  835. /////////////////////////////////////////////////////////////////
  836. #pragma mark ----- Internet Setup Documentation ------
  837.  
  838. /*
  839.     The mapping from NSHTCPv4ConfigurationDigest fields to preferences
  840.     is as follows:
  841.     
  842.     fProtocol            -> kOTCfgTypeTCPv4
  843.     fConfigName;        -> 'pnam'
  844.     fPortRef;            -> 'port', 'iitf'
  845.     fUnloadAttr;        -> 'unld'
  846.     fDNSServerList;        -> 'idns'
  847.     fLocalDomain;        -> 'ihst'
  848.     fAdminDomain;        -> 'ihst'
  849.     fConfigMethod;        -> 'iitf'
  850.     fIPAddress;            -> 'iitf'
  851.     fSubnetMask;        -> 'iitf'
  852.     fAppleTalkZone;        -> 'iitf'
  853.     fFraming;            -> 'iitf'
  854.     fSearchDomains;        -> 'isdm'
  855.     fRouterList;        -> 'irte'
  856.  
  857.     The reverse mapping, from preference to NSHTCPv4ConfigurationDigest field,
  858.     is:
  859.     
  860.     'pnam' -> fConfigName
  861.     'port' -> UserVisibleName(fPortRef)         -- or "AppleTalk (Mac IP)", localisation?
  862.     'pwrd' -> "\p"
  863.     'cvrs' -> 1
  864.     'prot' -> "tcp"
  865.     'unld' -> fUnloadAttr
  866.     'idns' -> GetHandleSize(fDNSServerList) div sizeof(InetHost), fDNSServerList;
  867.     'ihst' -> 1, fLocalDomain, fAdminDomain (packed)
  868.     'iitf' -> 1, fConfigMethod, fIPAddress, fSubnetMask, fAppleTalkZone, PortName(fPortRef), fModuleName(fPortRef), fFraming
  869.     'dtyp' -> DeviceType(fPortRef)            -- or kOTNoDevice for MacIP
  870.     'stng' -> $00 * 25
  871.     'isdm' -> fSearchDomains
  872.     'irte' -> fRouterList, padded appropriately
  873. */
  874.  
  875. /*
  876.     Mapping from NSHRemoteConfigurationDigest fields to preference types:
  877.     
  878.     fProtocol;                            kOTCfgTypeRemote
  879.     fConfigName;                        -> 'pnam'
  880.     fGuestLogin                            -> 'conn' (isGuest)
  881.     fPasswordValid                        -> 'conn' (passwordSaved)
  882.     fUserName;                            -> 'cusr'
  883.     fPassword;                            -> 'pass'
  884.     fPhoneNumber;                        -> 'cadr', 'conn' (addressLength)
  885.     fRedialMode;                        -> 'cdia' (dialMode)
  886.     fRedialTimes;                        -> 'cdia' (redialTries)
  887.     fRedialDelay;                        -> 'cdia' (redialDelay)
  888.     fAlternatePhoneNumber;                -> 'cead'
  889.     fVerboseLogging;                    -> 'logo' (logLevel)
  890.     fFlashIconWhileConnected;            -> 'conn' (flashConnectedIcon)
  891.     fPromptToRemainConnected;            -> 'conn' (issueConnectedReminders)
  892.     fPromptInterval;                    -> 'conn' (reminderMinutes)
  893.     fDisconnectIfIdle;                    -> 'ipcp' (idleTimerEnabled)
  894.     fDisconnectInterval;                -> 'ipcp' (idleTimerMilliseconds)
  895.     fSerialProtocol;                    -> 'conn' (serialProtocolMode)
  896.     fPPPConnectAutomatically;            -> 'cmsc' (isAutoConnect)
  897.     fPPPAllowModemCompression;            -> 'conn' (allowModemDataCompression)
  898.     fPPPAllowTCPIPHeaderCompression;    -> 'ipcp' (compressTCPHeaders)
  899.     fPPPConnectMode;                    -> 'conn' (chatMode)
  900.     fPPPConnectScriptName;                -> 'conn' (chatScriptName)
  901.     fPPPConnectScript;                    -> 'ccha', 'conn' (chatScriptLength)
  902.     
  903.     fixed value (see below)                -> 'lcp '
  904.     $0002 $0003 "Script" [36]            -> 'arap'
  905.     $0002 $0003    $00 * 596                -> 'x25 '
  906.     $0002 $0003 $00 * 68                -> 'dass'
  907.     $00010000 $00000001 $00 * 256        -> 'usmd'
  908.     $0002 $0003 $00 * 64                -> 'clks'
  909.     
  910.     This is the mapping from preference type to NSHRemoteConfigurationDigest field.
  911.  
  912.     'pnam' -> fConfigName
  913.     'cmsc' -> fPPPConnectAutomatically
  914.     'conn' -> fGuestLogin, fPasswordValid, fPhoneNumber, fFlashIconWhileConnected, fPromptToRemainConnected, fPromptInterval, fSerialProtocol, fPPPAllowModemCompression, fPPPConnectMode, fPPPConnectScriptName, fPPPConnectScript
  915.     'cusr' -> fUserName
  916.     'pass' -> fPassword
  917.     'cdia' -> fRedialMode, fRedialTimes, fRedialDelay
  918.     'ipcp' -> fDisconnectIfIdle, fDisconnectInterval, fPPPAllowTCPIPHeaderCompression
  919.     'logo' -> fVerboseLogging
  920.     'cadr' -> fPhoneNumber
  921.     'cead' -> fAlternatePhoneNumber
  922.     'ccha' -> fPPPConnectScript
  923.  
  924.     'usmd' -> junk
  925.     'clks' -> junk
  926.     'lcp ' -> junk
  927.     'arap' -> junk
  928.     'dass' -> junk
  929.     'x25 ' -> junk
  930.     'term' -> junk
  931.     'cnam' -> junk
  932.     'resn' -> junk
  933.     'resi' -> junk
  934.     
  935. */
  936.  
  937. // This fixed value for the Remote Access 'lcp ' preference is only used if
  938. // OTCfgGetDefault is not available.
  939.  
  940. static UInt8 gRemoteLCPValue[108] = {
  941.     0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69,
  942.     0x70, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  943.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  944.     0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A,
  945.     0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x00, 0x05,
  946.     0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x11, 0x94, 0x00, 0x00, 0x00, 0x00,
  947.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  948. };
  949.  
  950. /*
  951.     This is the mapping from preference type to NSHModemConfigurationDigest field.
  952.  
  953.     'pnam' -> fConfigName
  954.     'ccl ' -> fPortRef, fModemScript, fDialToneMode, fSpeakerOn, fPulseDial, fHintPortName
  955.     'lkmd' -> $00000001 + $00000000 * 4
  956.     'mdpw' -> $00 * 256
  957.     
  958.     'cnam' -> junk
  959.     'resn' -> junk
  960.     'resi' -> junk
  961. */
  962.  
  963. /////////////////////////////////////////////////////////////////
  964. #pragma mark ----- Packed Pref Builder/Writer Infrastructure ------
  965.  
  966. // All the code that manipulates groups of preferences, ie an entity
  967. // in a Network Setup sense, uses "packed preferences".  This is
  968. // a group of preferences packed into a handle.  Each preference
  969. // contains a header of its type (OSType) and its data size (ByteCount),
  970. // not including the header itself.  The handle is terminated by
  971. // an entry with a null preference type.
  972. //
  973. // The following routines are an easy way to build and parse these
  974. // packed preferences.  The key focus here was to minimise error
  975. // checked.  So when you build a packed preference, all the checking
  976. // is done inside these helper routines and you only have to check
  977. // for errors at the end.  Similarly, when you parse a packed preference
  978. // handle, you know it was built successfully so you can rely on its
  979. // structure.
  980. //
  981. // You typically use these routines in the following way.  First,
  982. // call BuilderNew to initialise the PrefBuilderState record.  Then
  983. // call BuilderNewPref to add a new preference to the handle.  If
  984. // necessary, you can also call BuilderAddPrefData to add extra
  985. // data to the most recently added preference.  Finally, call
  986. // BuilderDone to extract the packed preference handle, or obtain
  987. // any error that might have happened while building.
  988.  
  989. // When building a packed preference, the following state record
  990. // is used to keep track of what's going on.
  991.  
  992. struct PrefBuilderState {
  993.     Handle         prefData;                        // handle of packed preference itself, or nil if we got an error somewhere
  994.     OSStatus      latchedError;                    // if an error occured, this field is used to store it until BuilderDone is called
  995.     ByteCount     offsetToMostRecentPrefSize;        // offset to most recent pref size value,
  996.                                                 // allows BuilderAddPrefData to look back in the handle and bump this size
  997. };
  998. typedef struct PrefBuilderState PrefBuilderState;
  999.  
  1000. static void BuilderError(PrefBuilderState *state, OSStatus errNum)
  1001.     // This routine is called when an error happens while building.
  1002.     // It disposes of the packed preference handle (which ensures
  1003.     // that no more building is done) and latches the error where
  1004.     // BuilderDone can find it.
  1005. {
  1006.     DisposeHandle(state->prefData);
  1007.     MoreAssertQ(MemError() == noErr);
  1008.     state->prefData = nil;
  1009.     state->latchedError = errNum;
  1010. }
  1011.  
  1012. static void BuilderNew(PrefBuilderState *state)
  1013.     // Initialise the builder state record.  See above for
  1014.     // this routines place in the big picture.  If you call
  1015.     // this routine, you must also call BuilderDone to clean
  1016.     // up the builder (ie dispose of the handle and recover
  1017.     // the latched error code).
  1018. {
  1019.     OSStatus err;
  1020.     
  1021.     state->latchedError = noErr;
  1022.     state->offsetToMostRecentPrefSize = 0;
  1023.     state->prefData = NewHandle(0);
  1024.     err = MemError();
  1025.     if (err != noErr) {
  1026.         BuilderError(state, err);
  1027.     }
  1028. }
  1029.  
  1030. static void BuilderAddPrefData(PrefBuilderState *state, const void *data, ByteCount size)
  1031.     // Add preference data, described by data and size, to the previous
  1032.     // preference added to the builder.  It's an error to call this without
  1033.     // first calling BuilderNewPref.
  1034. {
  1035.     OSStatus err;
  1036.     
  1037.     if (state->prefData != nil) {
  1038.         
  1039.         // If this assert fires, it means you've called this routine without first calling BuilderNewPref.
  1040.         
  1041.         MoreAssertQ(state->offsetToMostRecentPrefSize != 0);
  1042.         
  1043.         // Add the data to the preference handle.
  1044.         
  1045.         err = PtrAndHand(data, state->prefData, size);
  1046.         
  1047.         // Reach back into the preference handle, find the size
  1048.         // of the current preference, and increment it by the amount
  1049.         // of data we added.  Note that this might fail on a 68000
  1050.         // processor (because it can't handle memory accesses to words
  1051.         // off word boundaries) but this isn't an issue because OT
  1052.         // requires an 68030 or above.
  1053.         
  1054.         if (err == noErr) {
  1055.             *((ByteCount *)((*(state->prefData)) + state->offsetToMostRecentPrefSize)) += size;
  1056.         }
  1057.         
  1058.         // Handle errors.
  1059.         
  1060.         if (err != noErr) {
  1061.             BuilderError(state, err);
  1062.         }
  1063.     }
  1064. }
  1065.  
  1066. static void BuilderNewPref(PrefBuilderState *state, OSType prefType, const void *data, ByteCount size)
  1067.     // Add a new preference to the builder, with the data specified
  1068.     // by data and size.  You can pass nil and 0 to these parameters
  1069.     // to add an empty preference.  Regardless, you can later call
  1070.     // BuilderAddPrefData to add extra data to this preference, up
  1071.     // until you call BuilderNewPref again.
  1072. {
  1073.     OSStatus err;
  1074.     ByteCount initialPrefSize;
  1075.     
  1076.     if (state->prefData != nil) {
  1077.  
  1078.         // Add the preference type to the preference handle.
  1079.         
  1080.         err = PtrAndHand(&prefType, state->prefData, sizeof(prefType));
  1081.         
  1082.         // Record the current offset into the preference handle
  1083.         // (which is the offset of this preference size value,
  1084.         // which is needed by BuilderAddPrefData) and then add
  1085.         // a default size value of 0 to the preference.
  1086.         
  1087.         if (err == noErr) {
  1088.             state->offsetToMostRecentPrefSize = GetHandleSize(state->prefData);
  1089.             initialPrefSize = 0;
  1090.             err = PtrAndHand(&initialPrefSize, state->prefData, sizeof(initialPrefSize));
  1091.         }
  1092.         
  1093.         // Handle errors.
  1094.         
  1095.         if (err != noErr) {
  1096.             BuilderError(state, err);
  1097.         }
  1098.     }
  1099.     
  1100.     // Finally, call BuilderAddPrefData to add the actual
  1101.     // preference data.  Note that if the above errored, the
  1102.     // error will have been latched in the state record, and
  1103.     // this call will be a no-op.
  1104.     
  1105.     if (size != 0) {
  1106.         BuilderAddPrefData(state, data, size);
  1107.     }
  1108. }
  1109.  
  1110. static OSStatus BuilderDone(PrefBuilderState *state, Handle *prefData)
  1111.     // Extracts the prefData from the builder state.  You must
  1112.     // pass in a pointer to a nil handle.  If building was successful,
  1113.     // this routine returns noErr and sets the handle to be
  1114.     // the built packed preferences.  From there on, the memory
  1115.     // belongs to you.
  1116.     //
  1117.     // If the building was unsuccessful, the routine returns an error
  1118.     // and the handle remains nil.
  1119.     
  1120. {
  1121.     OSStatus err;
  1122.     
  1123.     MoreAssertQ(prefData != nil);
  1124.  
  1125.     // Add a sentinel null OSType to the data handle.
  1126.     
  1127.     BuilderNewPref(state, 0, nil, 0);
  1128.     
  1129.     // Return the latched error code.
  1130.     
  1131.     if (state->prefData == nil) {
  1132.         err = state->latchedError;
  1133.     } else {
  1134.         err = noErr;
  1135.     }
  1136.     *prefData = state->prefData;
  1137.     
  1138.     MoreAssertQ( err == noErr && *prefData != nil || err != noErr && *prefData == nil );
  1139.     
  1140.     return err;
  1141. }
  1142.  
  1143. static void WriterGetNextPref(ByteCount *cookie, Ptr prefData, OSType *prefType, void **prefPtr, ByteCount *prefSize)
  1144.     // This routine provides a simple way for you to iterate over
  1145.     // a group of packed preferences.  You get the first preference
  1146.     // by setting *cookie to 0 and calling the routine.  The routine
  1147.     // returns the preference, and updates *cookie so that next time
  1148.     // you call it you get the second preference, and so on.
  1149.     //
  1150.     // prefData must point to a packed preference structure, as described
  1151.     // in the comments at the start of this section.  Typically you get
  1152.     // this by locking and dereferencing the handle returned by BuilderDone.
  1153.     // [Locking the handle is advised by not strictly necessary as
  1154.     // long as you take care not to move or purge between when this routine
  1155.     // returns and when you use *prefPtr.]  You don't have to pass in
  1156.     // the size of this handle because it's terminated by a null preference
  1157.     // type.  You can keep calling this routine until *prefType is returned
  1158.     // as 0, in which case you've hit the end of the packed preferences.
  1159.     //
  1160.     // The routine sets *prefType to the type of the preference, and
  1161.     // *prefPtr and *prefSize to point to the preference data itself.
  1162.     // Note that *prefPtr is not necessarily aligned to any memory
  1163.     // boundary, so if you access it as a word or long pointer on an
  1164.     // original 68000 you may cause an address error.
  1165. {
  1166.     MoreAssertQ(cookie != nil);
  1167.     MoreAssertQ(prefData != nil);
  1168.     MoreAssertQ(prefType != nil);
  1169.     MoreAssertQ(prefSize != nil);
  1170.     MoreAssertQ(prefPtr != nil);
  1171.  
  1172.     *prefType = *((OSType *)(prefData + *cookie));
  1173.     *cookie += sizeof(OSType);                            // skip cookie past the prefType
  1174.     *prefSize = *((ByteCount *)(prefData + *cookie));
  1175.     *cookie += sizeof(ByteCount);                        // skip cookie past the prefSize
  1176.     *prefPtr = prefData + *cookie;
  1177.     *cookie += *prefSize;                                // skip cookie past the data
  1178. }
  1179.  
  1180. static Handle NSHGetDefaultPreference(ResType entityType, ResType entityClass, ResType recordType)
  1181.     // Returns a handle containing a default value for the preference
  1182.     // of the given entity, class and preference type, or nil if there
  1183.     // is not such value (or there isn't enough memory to get it).
  1184.     // The result is a memory handle, for which you are responsible for
  1185.     // disposing.
  1186.     //
  1187.     // I decided not to export this routine because of its limited value
  1188.     // to external clients.  But it follows the standard outline used
  1189.     // by external routines.
  1190. {
  1191.     Handle result;
  1192.     
  1193.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  1194.         #if TARGET_RT_MAC_CFM
  1195.             result = OTCfgGetDefault(entityType, entityClass, recordType);
  1196.         #else
  1197.             // Network Setup has no Mixed Mode glue.  When running
  1198.             // code on a PowerPC with Network Setup available, you
  1199.             // should either compile your code as Fat or, if that's
  1200.             // infeasible, write your own Mixed Mode glue.
  1201.             return nil;
  1202.         #endif
  1203.     } else {
  1204.         if ( entityType == kOTCfgTypeRemote && entityClass == kOTCfgClassNetworkConnection && recordType == kOTCfgRemoteLCPPref) {
  1205.             if ( PtrToHand(gRemoteLCPValue, &result, sizeof(gRemoteLCPValue)) != noErr ) {
  1206.                 result = nil;
  1207.             }
  1208.         } else {
  1209.             result = nil;
  1210.         }
  1211.     }
  1212.     return result;
  1213. }
  1214.  
  1215. /////////////////////////////////////////////////////////////////
  1216. #pragma mark ----- TCP/IP Packer/Unpacker ------
  1217.  
  1218. // These routines convert between the TCP/IP configuration digest
  1219. // (a big record with all the relevant fields in it) and the
  1220. // packed preferences data (which is read from or written to the
  1221. // preferences store).  These routines are shared between the
  1222. // preferences database and preferences file implementations.
  1223.  
  1224. static const char *gProtocolName = "tcp";
  1225.  
  1226. static OSStatus BuildPackedPrefsFromTCPv4ConfigurationDigest(
  1227.                                     const NSHTCPv4ConfigurationDigest *configurationDigest, 
  1228.                                     Boolean forceDefaults,
  1229.                                     Handle *packedPrefs)
  1230.     // This routine converts a TCP/IP configuration digest
  1231.     // to a handle containing packed preferences.  The forceDefaults
  1232.     // parameter controls what happens to handle-based fields,
  1233.     // ie Handle fields in the digest.  Normally, if such a field
  1234.     // is nil, it's taken to mean "don't change".  However, if
  1235.     // forceDefaults is true, a nil means "write default value".
  1236.     // This happens when we're creating a new configuration,
  1237.     // where we want to make sure that empty default preferences
  1238.     // are created for these handle-based fields.
  1239. {
  1240.     OSStatus err;
  1241.     OTPortRecord portRec;
  1242.     Str255 portUserVisibleName;
  1243.     Boolean isMacIP;
  1244.     Str63 portName;
  1245.     Str63 moduleName;
  1246.     UInt8 tmpUInt8;
  1247.     UInt16 tmpUInt16;
  1248.     const char *kMacIPUserVisibleName = "AppleTalk (Mac IP)";
  1249.     static UInt8 zeros[25] = {0};
  1250.     PrefBuilderState state;    
  1251.     SInt8 s;
  1252.  
  1253.     MoreAssertQ(configurationDigest != nil);
  1254.     MoreAssertQ(packedPrefs != nil);
  1255.     
  1256.     *packedPrefs = nil;
  1257.     
  1258.     err = noErr;
  1259.     if ( OTFindPortByRef(&portRec, configurationDigest->fPortRef) ) {
  1260.         portName[0] = OTStrLength(portRec.fPortName);
  1261.         BlockMoveData(portRec.fPortName, &portName[1], portName[0]);
  1262.         moduleName[0] = OTStrLength(portRec.fModuleName);
  1263.         BlockMoveData(portRec.fModuleName, &moduleName[1], moduleName[0]);
  1264.     } else {
  1265.         err = kOTNotFoundErr;
  1266.     }
  1267.     
  1268.     if (err == noErr) {
  1269.         BuilderNew(&state);
  1270.  
  1271.     // 'pnam'
  1272.     
  1273.         BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1);
  1274.  
  1275.     // 'port'
  1276.  
  1277.         isMacIP = OTStrEqual(portRec.fModuleName, "ddp");
  1278.         if (isMacIP) {
  1279.             portUserVisibleName[0] = OTStrLength(kMacIPUserVisibleName);
  1280.             BlockMoveData(kMacIPUserVisibleName, &portUserVisibleName[1], portUserVisibleName[0]);
  1281.         } else {
  1282.             OTGetUserPortNameFromPortRef(configurationDigest->fPortRef, portUserVisibleName);
  1283.         }
  1284.         BuilderNewPref(&state, kOTCfgPortUserVisibleNamePref, portUserVisibleName, portUserVisibleName[0] + 1);
  1285.  
  1286.     // 'pwrd'
  1287.     
  1288.         tmpUInt8 = 0;
  1289.         BuilderNewPref(&state, kOTCfgAdminPasswordPref, &tmpUInt8, sizeof(tmpUInt8));
  1290.     
  1291.     // 'cvrs'
  1292.     
  1293.         tmpUInt16 = 1;
  1294.         BuilderNewPref(&state, kOTCfgVersionPref, &tmpUInt16, sizeof(tmpUInt16));
  1295.     
  1296.     // 'prot'
  1297.     
  1298.         BuilderNewPref(&state, kOTCfgProtocolUserVisibleNamePref, gProtocolName, OTStrLength(gProtocolName) + 1);
  1299.     
  1300.     // 'unld'
  1301.     
  1302.         BuilderNewPref(&state, kOTCfgTCPUnloadAttrPref, &configurationDigest->fUnloadAttr, sizeof(configurationDigest->fUnloadAttr));
  1303.     
  1304.     // 'idns'
  1305.     
  1306.         if (configurationDigest->fDNSServerList != nil) {
  1307.             UInt16 numServers;
  1308.             
  1309.             MoreAssertQ(GetHandleSize(configurationDigest->fDNSServerList) % sizeof(InetHost) == 0);
  1310.             numServers = GetHandleSize(configurationDigest->fDNSServerList) / sizeof(InetHost);
  1311.  
  1312.             BuilderNewPref(&state, kOTCfgTCPDNSServersListPref, &numServers, sizeof(numServers));
  1313.             s = HGetState(configurationDigest->fDNSServerList);
  1314.             HLock(configurationDigest->fDNSServerList);
  1315.             BuilderAddPrefData(&state, *configurationDigest->fDNSServerList, GetHandleSize(configurationDigest->fDNSServerList));
  1316.             HSetState(configurationDigest->fDNSServerList, s);
  1317.         } else if (forceDefaults) {
  1318.             tmpUInt16 = 0;
  1319.             BuilderNewPref(&state, kOTCfgTCPDNSServersListPref, &tmpUInt16, sizeof(tmpUInt16));
  1320.         }
  1321.     
  1322.     // 'ihst'
  1323.  
  1324.         tmpUInt8 = 1;
  1325.         BuilderNewPref(&state, kOTCfgTCPSearchListPref, &tmpUInt8, sizeof(tmpUInt8));
  1326.         BuilderAddPrefData(&state, configurationDigest->fLocalDomain, configurationDigest->fLocalDomain[0] + 1);
  1327.         BuilderAddPrefData(&state, configurationDigest->fAdminDomain, configurationDigest->fAdminDomain[0] + 1);
  1328.     
  1329.     // 'iitf'
  1330.  
  1331.         tmpUInt16 = 1;
  1332.         BuilderNewPref(&state, kOTCfgTCPInterfacesPref, &tmpUInt16, sizeof(tmpUInt16));
  1333.         BuilderAddPrefData(&state, &configurationDigest->fConfigMethod, sizeof(configurationDigest->fConfigMethod));
  1334.         BuilderAddPrefData(&state, &configurationDigest->fIPAddress, sizeof(configurationDigest->fIPAddress));
  1335.         BuilderAddPrefData(&state, &configurationDigest->fSubnetMask, sizeof(configurationDigest->fSubnetMask));
  1336.         if (isMacIP) {
  1337.             BuilderAddPrefData(&state, configurationDigest->fAppleTalkZone, configurationDigest->fAppleTalkZone[0] + 1);
  1338.             BuilderAddPrefData(&state, "\pddp", 36);
  1339.         } else {
  1340.             BuilderAddPrefData(&state, "\p*", 2);
  1341.             BuilderAddPrefData(&state, portName, 36);
  1342.         }
  1343.         BuilderAddPrefData(&state, moduleName, 32);
  1344.         BuilderAddPrefData(&state, &configurationDigest->fFraming, sizeof(configurationDigest->fFraming));
  1345.         
  1346.     // 'dtyp'
  1347.     
  1348.         if (isMacIP) {
  1349.             tmpUInt16 = kOTNoDeviceType;
  1350.         } else {
  1351.             tmpUInt16 = OTGetDeviceTypeFromPortRef(configurationDigest->fPortRef);
  1352.         }
  1353.         BuilderNewPref(&state, kOTCfgTCPDeviceTypePref, &tmpUInt16, sizeof(tmpUInt16));
  1354.  
  1355.     // 'stng'
  1356.     
  1357.         BuilderNewPref(&state, kOTCfgTCPLocksPref, zeros, sizeof(zeros));
  1358.         
  1359.     // 'isdm'
  1360.     
  1361.         if (configurationDigest->fSearchDomains != nil) {
  1362.             s = HGetState(configurationDigest->fSearchDomains);
  1363.             HLock(configurationDigest->fSearchDomains);
  1364.             BuilderNewPref(&state, kOTCfgTCPSearchDomainsPref, *configurationDigest->fSearchDomains, GetHandleSize(configurationDigest->fSearchDomains));
  1365.             HSetState(configurationDigest->fSearchDomains, s);
  1366.         } else if (forceDefaults) {
  1367.             tmpUInt16 = 0;
  1368.             BuilderNewPref(&state, kOTCfgTCPSearchDomainsPref, &tmpUInt16, sizeof(tmpUInt16));
  1369.         }
  1370.  
  1371.     // 'irte'
  1372.  
  1373.         if (configurationDigest->fRouterList != nil || forceDefaults) {
  1374.             UInt16 numRouters;
  1375.             UInt16 routerIndex;
  1376.  
  1377.             if (configurationDigest->fRouterList == nil) {
  1378.                 numRouters = 0;
  1379.             } else {
  1380.                 MoreAssertQ(GetHandleSize(configurationDigest->fRouterList) % sizeof(InetHost) == 0);
  1381.                 numRouters = GetHandleSize(configurationDigest->fRouterList) / sizeof(InetHost);
  1382.             }
  1383.             BuilderNewPref(&state, kOTCfgTCPRoutersListPref, &numRouters, sizeof(numRouters));
  1384.             for (routerIndex = 0; routerIndex < numRouters; routerIndex++) {
  1385.                 OTCfgTCPRoutersListEntry entry;
  1386.                 
  1387.                 entry.fToHost = 0;
  1388.                 entry.fViaHost = (*((InetHost **) configurationDigest->fRouterList))[routerIndex];
  1389.                 entry.fLocal = 0;
  1390.                 entry.fHost = 0;
  1391.                 BuilderAddPrefData(&state, &entry, sizeof(entry));
  1392.             }
  1393.         }
  1394.  
  1395.         err = BuilderDone(&state, packedPrefs);
  1396.     }
  1397.  
  1398.     return err;
  1399. }
  1400.  
  1401. static Boolean UnpackTCPSearchList(const UInt8 *data, ByteCount length, NSHTCPv4ConfigurationDigest *configurationDigest)
  1402.     // This routine is used to unpacked the TCP search list preference
  1403.     // (kOTCfgTCPSearchListPref == 'ihst').  The preference is a weird
  1404.     // combination of packed strings and straight data, so we have to
  1405.     // mess around a bit.  The routine returns true if it could
  1406.     // parse the data successfully; false if it found a formatting
  1407.     // error in the data.
  1408. {
  1409.     const UInt8 *cursor;
  1410.     UInt8 primaryInterfaceIndex;
  1411.     MoreAssertQ(data != nil);
  1412.     MoreAssertQ(configurationDigest != nil);
  1413.     
  1414.     cursor = data;
  1415.     if (cursor + sizeof(SInt8) <= data + length) {
  1416.         primaryInterfaceIndex = *cursor;
  1417.         cursor += sizeof(SInt8);
  1418.     }
  1419.     if (cursor + *cursor + 1 <= data + length) {
  1420.         BlockMoveData(cursor, configurationDigest->fLocalDomain, *cursor + 1);
  1421.         cursor += (*cursor + 1);
  1422.     }
  1423.     if (cursor + *cursor + 1 <= data + length) {
  1424.         BlockMoveData(cursor, configurationDigest->fAdminDomain, *cursor + 1);
  1425.         cursor += (*cursor + 1);
  1426.     }
  1427.     return (primaryInterfaceIndex == 1) && (cursor == data + length);
  1428. }
  1429.  
  1430. static void UnpackTCPPrefs(Ptr *buffer, NSHTCPv4ConfigurationDigest *configurationDigest)
  1431.     // This routine unpacks an interface from an 'iitf' (kOTCfgTCPInterfacesPref)
  1432.     // preference into the relevant fields of a NSHTCPv4ConfigurationDigest.
  1433.     // *buffer must point to the beginning
  1434.     // of the interface, ie two bytes into the pref data if
  1435.     // if you're extracting the first interface.  *buffer
  1436.     // is updated to point to the byte after the last byte
  1437.     // parsed, so you can parse multiple interfaces by
  1438.     // repeatedly calling this routine.  You can also
  1439.     // check *buffer to determine whether the routine
  1440.     // consumed all the data you expected, and hence whether
  1441.     // the preference is formatted correctly.
  1442. {
  1443.     UInt8 *cursor;
  1444.     
  1445.     cursor = (UInt8 *) *buffer;
  1446.     
  1447.     configurationDigest->fConfigMethod = *cursor;
  1448.     cursor += sizeof(UInt8);
  1449.     configurationDigest->fIPAddress = *((InetHost *) cursor);
  1450.     cursor += sizeof(InetHost);
  1451.     configurationDigest->fSubnetMask = *((InetHost *) cursor);
  1452.     cursor += sizeof(InetHost);
  1453.  
  1454.     // fAppleTalkZone is a Str32.  A longer string in the
  1455.     // 'iitf' is a bug in the person who wrote the code and
  1456.     // causes us to stop parsing.  The caller will notice that 
  1457.     // the cursor did not advance far enough and error out.
  1458.     
  1459.     if ( *cursor <= 32 ) {
  1460.         BlockMoveData(cursor, configurationDigest->fAppleTalkZone, *cursor + 1);
  1461.         cursor += (*cursor + 1);
  1462.         BlockMoveData(cursor, configurationDigest->fHintPortName, 36);
  1463.         cursor += 36;
  1464.         BlockMoveData(cursor, configurationDigest->fHintDriverName, 32);
  1465.         cursor += 32;
  1466.         configurationDigest->fFraming = *((UInt32 *) cursor);
  1467.         cursor += sizeof(UInt32);
  1468.     }
  1469.  
  1470.     *buffer = (Ptr) cursor;
  1471. }
  1472.  
  1473. static Boolean PortMatchesTCPv4Hints(const OTPortRecord *portRec, const NSHTCPv4ConfigurationDigest *hints)
  1474.     // This routine checks whether a particular port matches the set
  1475.     // of hints extracted from the TCP/IP preferences, and returns true
  1476.     // if it does.  The hints include:
  1477.     //
  1478.     //   a) the user-visible name of the port,
  1479.     //   b) the port name itself,
  1480.     //   c) the name of the module controlling the part, and
  1481.     //   d) the device type of the module controlling the port.
  1482. {
  1483.     Str255 userVisibleName;
  1484.  
  1485.     OTGetUserPortNameFromPortRef(portRec->fRef, userVisibleName);
  1486.     return         OTMemcmp(userVisibleName, hints->fHintUserVisiblePortName, userVisibleName[0] + 1)
  1487.             &&    OTStrEqual(portRec->fPortName, ((char *) hints->fHintPortName) + 1)
  1488.             &&    OTStrEqual(portRec->fModuleName, ((char *) hints->fHintDriverName) + 1)
  1489.             &&    OTGetDeviceTypeFromPortRef(portRec->fRef) == hints->fHintDeviceType;
  1490. }
  1491.  
  1492. static void ClearTCPv4ConfigurationDigest(NSHTCPv4ConfigurationDigest *configurationDigest)
  1493.     // Clear out the entire parameter block, preserving the handle-based fields.
  1494. {
  1495.     Handle saveRouterList;
  1496.     Handle saveDNSServerList;
  1497.     Handle saveSearchDomains;
  1498.  
  1499.     saveRouterList = configurationDigest->fRouterList;
  1500.     saveDNSServerList = configurationDigest->fDNSServerList;
  1501.     saveSearchDomains = configurationDigest->fSearchDomains;
  1502.     OTMemzero(configurationDigest, sizeof(configurationDigest));
  1503.     configurationDigest->fRouterList = saveRouterList;
  1504.     configurationDigest->fDNSServerList = saveDNSServerList;
  1505.     configurationDigest->fSearchDomains = saveSearchDomains;
  1506. }
  1507.  
  1508. static OSStatus BuildTCPv4ConfigurationDigestFromPackedPrefs(Handle packedPrefs,                                     
  1509.                                     NSHTCPv4ConfigurationDigest *configurationDigest)
  1510.     // This routine fills out a TCP/IP configuration digest
  1511.     // based on the packed preferences.  The basic algorithm
  1512.     // is to iterate through all the preferences, looking at
  1513.     // each one and putting the data from that preference into
  1514.     // the configuration digest.  At the end it does some jiggery
  1515.     // pokery that's explained in the comment down there.
  1516. {
  1517.     OSStatus err;
  1518.     SInt8 s;
  1519.     ByteCount cookie;
  1520.     OSType prefType;
  1521.     ByteCount prefSize;
  1522.     void *prefData;
  1523.     UInt16 numServers;
  1524.     UInt16 numRouters;
  1525.     UInt16 routerIndex;
  1526.     OTPortRecord portRec;
  1527.     
  1528.     MoreAssertQ(packedPrefs != nil);
  1529.     MoreAssertQ(configurationDigest != nil);
  1530.     
  1531.     ClearTCPv4ConfigurationDigest(configurationDigest);
  1532.     
  1533.     configurationDigest->fProtocol = kOTCfgTypeTCPv4;
  1534.     
  1535.     s = HGetState(packedPrefs);
  1536.     HLock(packedPrefs);
  1537.     
  1538.     err = noErr;
  1539.     cookie = 0;
  1540.     do {
  1541.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize);
  1542.         switch (prefType) {
  1543.             case 0:
  1544.                 // do nothing, this is the loop exit condition
  1545.                 break;
  1546.         
  1547.         // 'pnam'
  1548.         
  1549.             case kOTCfgUserVisibleNamePref:
  1550.                 if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) {
  1551.                     BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255));
  1552.                 } else {
  1553.                     err = -8;
  1554.                 }
  1555.                 break;
  1556.         
  1557.         // 'port'
  1558.         
  1559.             case kOTCfgPortUserVisibleNamePref:
  1560.                 if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) {
  1561.                     BlockMoveData(prefData, configurationDigest->fHintUserVisiblePortName, sizeof(Str255));
  1562.                 } else {
  1563.                     err = -8;
  1564.                 }
  1565.                 break;
  1566.         
  1567.         // 'pwrd'
  1568.         
  1569.             case kOTCfgAdminPasswordPref:
  1570.                 // do nothing, we can ignore password prefs
  1571.                 break;
  1572.  
  1573.         // 'cvrs'
  1574.         
  1575.             case kOTCfgVersionPref:
  1576.                 // do nothing, except check that it's what we expect
  1577.                 if ( prefSize != sizeof(UInt16) || *((UInt16 *)prefData) != 1) {
  1578.                     err = -8;
  1579.                 }
  1580.                 break;
  1581.  
  1582.         // 'prot'
  1583.         
  1584.             case kOTCfgProtocolUserVisibleNamePref:
  1585.                 // do nothing, except check it's TCP/IP
  1586.                 if ( prefSize != OTStrLength(prefData) + 1 || ! OTStrEqual(prefData, gProtocolName) ) {
  1587.                     err = -8;
  1588.                 }
  1589.                 break;
  1590.  
  1591.         // 'unld'
  1592.         
  1593.             case kOTCfgTCPUnloadAttrPref:
  1594.                 configurationDigest->fUnloadAttr = *((UInt16 *)prefData);
  1595.                 if ( prefSize != sizeof(UInt16) 
  1596.                             || configurationDigest->fUnloadAttr < kOTCfgTCPActiveLoadedOnDemand
  1597.                             || configurationDigest->fUnloadAttr > kOTCfgTCPInactive ) {
  1598.                     err = -8;
  1599.                 }
  1600.                 break;
  1601.  
  1602.         // 'idns'
  1603.         
  1604.             case kOTCfgTCPDNSServersListPref:
  1605.                 numServers = *((UInt16 *)prefData);
  1606.                 if ( prefSize == sizeof(UInt16) + numServers * sizeof(InetHost) ) {
  1607.                     if (configurationDigest->fDNSServerList != nil) {
  1608.                         err = PtrToXHand( ((char *) prefData) + sizeof(UInt16), configurationDigest->fDNSServerList, prefSize - sizeof(UInt16));
  1609.                     }
  1610.                 } else {
  1611.                     err = -8;
  1612.                 }
  1613.                 break;
  1614.  
  1615.         // 'ihst'
  1616.         
  1617.             case kOTCfgTCPSearchListPref:
  1618.                 if ( ! UnpackTCPSearchList(prefData, prefSize, configurationDigest) ) {
  1619.                     err = -8;
  1620.                 }
  1621.                 break;
  1622.  
  1623.         // 'iitf'
  1624.         
  1625.             case kOTCfgTCPInterfacesPref:
  1626.                 if ( prefSize >= sizeof(UInt16) && *((UInt16 *)prefData) == 1 ) {
  1627.                     Ptr cursor;
  1628.                     
  1629.                     cursor = (Ptr) prefData;
  1630.                     cursor += sizeof(UInt16);
  1631.  
  1632.                     UnpackTCPPrefs(&cursor, configurationDigest);
  1633.                     
  1634.                     if ( cursor != ((Ptr) prefData) + prefSize ) {
  1635.                         err = -8;
  1636.                     }
  1637.                 } else {
  1638.                     err = -8;
  1639.                 }
  1640.                 break;
  1641.  
  1642.         // 'dtyp'
  1643.         
  1644.             case kOTCfgTCPDeviceTypePref:
  1645.                 if (prefSize == sizeof(SInt16)) {
  1646.                     configurationDigest->fHintDeviceType = *((UInt16 *)prefData);
  1647.                 } else {
  1648.                     err = -8;
  1649.                 }
  1650.                 break;
  1651.  
  1652.         // 'stng'
  1653.         
  1654.             case kOTCfgTCPLocksPref:
  1655.                 // The pref should be of size 25, but I've seen
  1656.                 // cases where it was set to 27.  I have no idea
  1657.                 // what causes this, but seeing as the size (or
  1658.                 // indeed the contents) doesn't matter for this
  1659.                 // program, I decided to handle that case.
  1660.                 if (prefSize != 25 && prefSize != 27) {
  1661.                     err = -8;
  1662.                 }
  1663.                 break;
  1664.  
  1665.         // 'isdm'
  1666.         
  1667.             case kOTCfgTCPSearchDomainsPref:
  1668.                 if (prefSize >= sizeof(UInt16)) {
  1669.                     if ( configurationDigest->fSearchDomains != nil ) {
  1670.                         err = PtrToXHand(prefData, configurationDigest->fSearchDomains, prefSize);
  1671.                         if (err == noErr) {
  1672.                             if ( ! ValidStringListHandle( configurationDigest->fSearchDomains ) ) {
  1673.                                 err = -8;
  1674.                             }
  1675.                         }
  1676.                     }
  1677.                 } else {
  1678.                     err = -8;
  1679.                 }
  1680.                 break;
  1681.  
  1682.         // 'irte'
  1683.         
  1684.             case kOTCfgTCPRoutersListPref:
  1685.                 numRouters = *((UInt16 *)prefData);
  1686.                 if ( prefSize == sizeof(UInt16) + numRouters * sizeof(OTCfgTCPRoutersListEntry) ) {
  1687.                     if (configurationDigest->fRouterList != nil) {
  1688.                         SetHandleSize(configurationDigest->fRouterList, numRouters * sizeof(InetHost));
  1689.                         err = MemError();
  1690.                         if (err == noErr) {
  1691.                             OTCfgTCPRoutersList *prefRouterList;
  1692.                             InetHost *digestRouterList;
  1693.  
  1694.                             prefRouterList = (OTCfgTCPRoutersList *) prefData;
  1695.                             digestRouterList = (InetHost *) *configurationDigest->fRouterList;
  1696.                             for (routerIndex = 0; routerIndex < numRouters; routerIndex++) {
  1697.                                 digestRouterList[routerIndex] = prefRouterList->fList[routerIndex].fViaHost;
  1698.                                 // Should really check that the other fields of
  1699.                                 // the OTCfgTCPRoutersListEntry are zero, but I can't be bothered
  1700.                                 // right now.
  1701.                             }
  1702.                         }
  1703.                     }
  1704.                 } else {
  1705.                     err = -8;
  1706.                 }
  1707.                 break;
  1708.         
  1709.             case kOTCfgUserModePref:            // user level
  1710.             case kOTCfgPrefWindowPositionPref:    // control panel window position
  1711.             case kOTCfgTCPDHCPClientIDPref:        // DHCP client ID
  1712.             case kOTCfgTCPDHCPLeaseInfoPref:    // DHCP persistent state
  1713.             case kOTCfgTCPPushBelowIPPref:        // below IP module
  1714.             case kOTCfgTCPPushBelowIPListPref:    // below IP module list (OT 2.5 and higher)
  1715.             case kOTCfgProtocolOptionsPref:        // wacky protocol options
  1716.             case 'vers':                        // occasionally you find these in TCP/IP configs, although they shouldn't be there
  1717.                 // Known preferences which we don't need to pay
  1718.                 // attention to but shouldn't cause the following
  1719.                 // assert to trigger.
  1720.                 break;
  1721.                 
  1722.             default:
  1723.                 // Unexpected preference type in the packed prefs.
  1724.                 // This is not super-fatal, but let the developer know in debug builds.
  1725.                 MoreAssertQ(false);
  1726.                 break;
  1727.         }
  1728.     } while (err == noErr && prefType != 0);
  1729.     
  1730.     HSetState(packedPrefs, s);
  1731.  
  1732.     // Before we leave, we have to set up the fPortRef field of the
  1733.     // configuration digest.  This is a tricky business because the
  1734.     // port that we last used might have been removed, or ejected,
  1735.     // and replaced by another remarkably similar port.  So we
  1736.     // only set up fPortRef is *all* of the hints we extracted from
  1737.     // the preferences match the port we found.  Otherwise, we
  1738.     // leave fPortRef set to 0 and expect the client to do the
  1739.     // matching based on the hints.
  1740.     //
  1741.     // Except, of course, for MacIP, where the algorithm is completely
  1742.     // different, as always.
  1743.     
  1744.     if (err == noErr) {
  1745.         // Put a null byte after the last byte of these two hints.
  1746.         // We know we have the space because they're declared as
  1747.         // Str63s.  We need this for later string comparisons,
  1748.         // both here and in PortMatchesTCPv4Hints.
  1749.         
  1750.         configurationDigest->fHintPortName[configurationDigest->fHintPortName[0] + 1] = 0;
  1751.         configurationDigest->fHintDriverName[configurationDigest->fHintDriverName[0] + 1] = 0;
  1752.         
  1753.         // Translation of the below:
  1754.         //
  1755.         // if the driver name was "ddp" and AppleTalk is active,
  1756.         //   return the port ref of the active AppleTalk port
  1757.         // else if we have a port which exactly matches all the hints,
  1758.         //   return the port ref of that port
  1759.         // else
  1760.         //   return 0
  1761.         // end-if
  1762.         
  1763.         if (     ( OTStrEqual(((char *) configurationDigest->fHintDriverName) + 1, "ddp")
  1764.                     && OTFindPort(&portRec, "ddp1") )
  1765.              || ( OTFindPort(&portRec, ((char *) configurationDigest->fHintPortName) + 1)
  1766.                     && PortMatchesTCPv4Hints(&portRec, configurationDigest) ) ) {
  1767.             configurationDigest->fPortRef = portRec.fRef;
  1768.         } else {
  1769.             configurationDigest->fPortRef = kOTInvalidPortRef;
  1770.         }
  1771.     }
  1772.     
  1773.     return err;
  1774. }
  1775.  
  1776. /////////////////////////////////////////////////////////////////
  1777. #pragma mark ----- Remote Access Packer/Unpacker ------
  1778.  
  1779. // These routines convert between the Remote Access configuration digest
  1780. // (a big record with all the relevant fields in it) and the
  1781. // packed preferences data (which is read from or written to the
  1782. // preferences store).  These routines are shared between the
  1783. // preferences database and preferences file implementations.
  1784.  
  1785. static Boolean RemoteVersionAcceptable(UInt32 version)
  1786.     // This routine returns true if the version number passed
  1787.     // in indicates that the preference was written by a
  1788.     // version of Remote Access whose preferences we understand.
  1789. {
  1790.     return version == kOTCfgRemoteDefaultVersion || version == kOTCfgRemoteAcceptedVersion;
  1791. }
  1792.  
  1793. static OSStatus BuildPackedPrefsFromRemoteConfigurationDigest(
  1794.                                     const NSHRemoteConfigurationDigest *configurationDigest, 
  1795.                                     Boolean forceDefaults,
  1796.                                     Handle *packedPrefs)
  1797.     // This routine converts a Remote Access configuration digest
  1798.     // to a handle containing packed preferences.
  1799.     //
  1800.     // forceDefaults is not needed for Remote Access because the
  1801.     // only handle-based field is an optional preference.
  1802. {
  1803.     #pragma unused(forceDefaults)
  1804.     OSStatus err;
  1805.     PrefBuilderState state;    
  1806.     UInt8 buffer[600];
  1807.     UInt32 tmpUInt32;
  1808.     Str255 encodedPassword;
  1809.     Handle prefH;
  1810.  
  1811.     MoreAssertQ(configurationDigest != nil);
  1812.     MoreAssertQ(packedPrefs != nil);
  1813.     
  1814.     *packedPrefs = nil;
  1815.     
  1816.     err = noErr;
  1817.     
  1818.     if (err == noErr) {
  1819.         BuilderNew(&state);
  1820.  
  1821.     // 'pnam'
  1822.     
  1823.         BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1);
  1824.  
  1825.     // 'conn'
  1826.  
  1827.         {
  1828.             OTCfgRemoteConnect *connectPtr;
  1829.             
  1830.             OTMemzero(buffer, sizeof(buffer));
  1831.             MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteConnect));
  1832.             connectPtr = (OTCfgRemoteConnect *) buffer;
  1833.  
  1834.             connectPtr->version = kOTCfgRemoteDefaultVersion;
  1835.             connectPtr->isGuest = configurationDigest->fGuestLogin;
  1836.             connectPtr->canInteract = true;
  1837.             connectPtr->passwordSaved = configurationDigest->fPasswordValid;
  1838.             connectPtr->flashConnectedIcon = configurationDigest->fFlashIconWhileConnected;
  1839.             connectPtr->issueConnectedReminders = configurationDigest->fPromptToRemainConnected;
  1840.             connectPtr->reminderMinutes = configurationDigest->fPromptInterval;
  1841.             connectPtr->allowModemDataCompression = configurationDigest->fPPPAllowModemCompression;
  1842.             connectPtr->chatMode = configurationDigest->fPPPConnectMode;
  1843.             connectPtr->serialProtocolMode = configurationDigest->fSerialProtocol;
  1844.  
  1845.             connectPtr->addressLength = configurationDigest->fPhoneNumber[0];
  1846.             BlockMoveData(configurationDigest->fPPPConnectScriptName, connectPtr->chatScriptName, sizeof(Str63));
  1847.             if ( configurationDigest->fPPPConnectScript != nil ) {
  1848.                 connectPtr->chatScriptLength = GetHandleSize(configurationDigest->fPPPConnectScript);
  1849.             }
  1850.             BuilderNewPref(&state, kOTCfgRemoteConnectPref, connectPtr, sizeof(*connectPtr));
  1851.         }
  1852.  
  1853.     // 'cusr'
  1854.     
  1855.         BuilderNewPref(&state, kOTCfgRemoteUserPref, configurationDigest->fUserName, configurationDigest->fUserName[0] + 1);
  1856.  
  1857.     // 'pass'
  1858.  
  1859.         err = NSHEncodeRemotePassword(configurationDigest->fUserName, configurationDigest->fPassword, encodedPassword);
  1860.         if (err == noErr) {
  1861.             BuilderNewPref(&state, kOTCfgRemotePasswordPref, encodedPassword, sizeof(Str255));
  1862.         } else {
  1863.             BuilderError(&state, err);
  1864.         }
  1865.  
  1866.     // 'cadr'
  1867.         
  1868.         BuilderNewPref(&state, kOTCfgRemoteAddressPref, &configurationDigest->fPhoneNumber[1], configurationDigest->fPhoneNumber[0]);
  1869.  
  1870.     // 'cdia'
  1871.     
  1872.         {
  1873.             OTCfgRemoteDialing *dialPtr;
  1874.             
  1875.             OTMemzero(buffer, sizeof(buffer));
  1876.             MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteDialing));
  1877.             dialPtr = (OTCfgRemoteDialing *) buffer;
  1878.  
  1879.             dialPtr->version = kOTCfgRemoteDefaultVersion;
  1880.             dialPtr->fType = 'dial';
  1881.             dialPtr->dialMode = configurationDigest->fRedialMode;
  1882.             dialPtr->redialTries = configurationDigest->fRedialTimes;
  1883.             dialPtr->redialDelay = configurationDigest->fRedialDelay;
  1884.  
  1885.             BuilderNewPref(&state, kOTCfgRemoteDialingPref, dialPtr, sizeof(*dialPtr));
  1886.         }
  1887.  
  1888.     // 'cead'
  1889.     
  1890.         if ( configurationDigest->fRedialMode == kOTCfgRemoteRedialMainAndAlternate ) {
  1891.             tmpUInt32 = 0;
  1892.             BuilderNewPref(&state, kOTCfgRemoteAlternateAddressPref, &tmpUInt32, sizeof(tmpUInt32));
  1893.             BuilderAddPrefData(&state, configurationDigest->fAlternatePhoneNumber, sizeof(Str255));
  1894.         }
  1895.  
  1896.     // 'logo'
  1897.     
  1898.         {
  1899.             OTCfgRemoteLogOptions *logPtr;
  1900.             
  1901.             OTMemzero(buffer, sizeof(buffer));
  1902.             MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteLogOptions));
  1903.             logPtr = (OTCfgRemoteLogOptions *) buffer;
  1904.  
  1905.             logPtr->version = kOTCfgRemoteDefaultVersion;
  1906.             logPtr->fType = 'lgop';
  1907.             logPtr->logLevel = configurationDigest->fVerboseLogging;
  1908.  
  1909.             BuilderNewPref(&state, kOTCfgRemoteLogOptionsPref, logPtr, sizeof(*logPtr));
  1910.         }
  1911.  
  1912.     // 'ipcp'
  1913.     
  1914.         {
  1915.             OTCfgRemoteIPCP *ipcpPtr;
  1916.             
  1917.             OTMemzero(buffer, sizeof(buffer));
  1918.             MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteIPCP));
  1919.             ipcpPtr = (OTCfgRemoteIPCP *) buffer;
  1920.  
  1921.             ipcpPtr->version = kOTCfgRemoteDefaultVersion;
  1922.             ipcpPtr->reserved[0] = 'ipcp';
  1923.             ipcpPtr->maxConfig = 10;
  1924.             ipcpPtr->maxTerminate = 10;
  1925.             ipcpPtr->maxFailureLocal = 10;
  1926.             ipcpPtr->maxFailureRemote = 10;
  1927.             ipcpPtr->timerPeriod = 10000;
  1928.             ipcpPtr->localIPAddress = 0;
  1929.             ipcpPtr->remoteIPAddress = 0;
  1930.             ipcpPtr->allowAddressNegotiation = 1;
  1931.             ipcpPtr->idleTimerEnabled = configurationDigest->fDisconnectIfIdle;
  1932.             ipcpPtr->compressTCPHeaders = configurationDigest->fPPPAllowTCPIPHeaderCompression;
  1933.             ipcpPtr->idleTimerMilliseconds = configurationDigest->fDisconnectInterval;
  1934.  
  1935.             BuilderNewPref(&state, kOTCfgRemoteIPCPPref, ipcpPtr, sizeof(*ipcpPtr));
  1936.         }
  1937.  
  1938.     // 'cmsc'
  1939.         
  1940.         tmpUInt32 = kOTCfgRemoteDefaultVersion;
  1941.         BuilderNewPref(&state, kOTCfgRemoteClientMiscPref, &tmpUInt32, sizeof(tmpUInt32));
  1942.         tmpUInt32 = configurationDigest->fPPPConnectAutomatically;
  1943.         BuilderAddPrefData(&state, &tmpUInt32, sizeof(tmpUInt32));
  1944.         
  1945.     // 'ccha'
  1946.     
  1947.         if ( configurationDigest->fPPPConnectScript != nil ) {
  1948.             SInt8 s;
  1949.             
  1950.             s = HGetState(configurationDigest->fPPPConnectScript);
  1951.             HLock(configurationDigest->fPPPConnectScript);
  1952.             
  1953.             BuilderNewPref(&state, kOTCfgRemoteChatPref, *configurationDigest->fPPPConnectScript, GetHandleSize(configurationDigest->fPPPConnectScript));
  1954.             
  1955.             HSetState(configurationDigest->fPPPConnectScript, s);
  1956.         }
  1957.  
  1958.     // 'lcp '
  1959.  
  1960.         prefH = NSHGetDefaultPreference(kOTCfgTypeRemote, kOTCfgClassNetworkConnection, kOTCfgRemoteLCPPref);
  1961.         if (prefH != nil) {
  1962.             MoreAssertQ(GetHandleSize(prefH) == sizeof(gRemoteLCPValue));
  1963.             HLock(prefH);
  1964.             MoreAssertQ(MemError() == noErr);
  1965.             BuilderNewPref(&state, kOTCfgRemoteLCPPref, *prefH, GetHandleSize(prefH));
  1966.             DisposeHandle(prefH);
  1967.             MoreAssertQ(MemError() == noErr);
  1968.         } else {
  1969.             BuilderError(&state, resNotFound);
  1970.         }
  1971.  
  1972.     // 'arap'
  1973.  
  1974.         tmpUInt32 = kOTCfgRemoteDefaultVersion;
  1975.         BuilderNewPref(&state, kOTCfgRemoteARAPPref, &tmpUInt32, sizeof(tmpUInt32));
  1976.         OTMemzero(buffer, sizeof(buffer));
  1977.         OTStrCopy( (char *) buffer, "Script");
  1978.         BuilderAddPrefData(&state, buffer, 36);
  1979.     
  1980.     // 'x25 '
  1981.         {
  1982.             OTCfgRemoteX25 *x25Ptr;
  1983.             
  1984.             OTMemzero(buffer, sizeof(buffer));
  1985.             MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteX25));
  1986.             x25Ptr = (OTCfgRemoteX25 *) buffer;
  1987.  
  1988.             x25Ptr->version = kOTCfgRemoteDefaultVersion;
  1989.  
  1990.             BuilderNewPref(&state, kOTCfgRemoteX25Pref, x25Ptr, sizeof(*x25Ptr));
  1991.         }
  1992.     
  1993.     // 'dass'
  1994.     
  1995.         tmpUInt32 = kOTCfgRemoteDefaultVersion;
  1996.         BuilderNewPref(&state, kOTCfgRemoteDialAssistPref, &tmpUInt32, sizeof(tmpUInt32));
  1997.         OTMemzero(buffer, sizeof(buffer));
  1998.         BuilderAddPrefData(&state, buffer, 68);
  1999.  
  2000.     // 'usmd'
  2001.     
  2002.         tmpUInt32 = kOTCfgRemoteDefaultVersion;
  2003.         BuilderNewPref(&state, kOTCfgRemoteUserModePref, &tmpUInt32, sizeof(tmpUInt32));
  2004.         tmpUInt32 = 0x00000001;
  2005.         BuilderAddPrefData(&state, &tmpUInt32, sizeof(tmpUInt32));
  2006.         OTMemzero(buffer, sizeof(buffer));
  2007.         BuilderAddPrefData(&state, buffer, 256);
  2008.  
  2009.     // 'clks'
  2010.  
  2011.         tmpUInt32 = kOTCfgRemoteDefaultVersion;
  2012.         BuilderNewPref(&state, kOTCfgRemoteClientLocksPref, &tmpUInt32, sizeof(tmpUInt32));
  2013.         OTMemzero(buffer, sizeof(buffer));
  2014.         BuilderAddPrefData(&state, buffer, 64);
  2015.     
  2016.         err = BuilderDone(&state, packedPrefs);
  2017.     }
  2018.  
  2019.     return err;
  2020. }
  2021.  
  2022. static void ClearRemoteConfigurationDigest(NSHRemoteConfigurationDigest *configurationDigest)
  2023.     // Clear out the entire parameter block, preserving the handle-based fields.
  2024. {
  2025.     Handle savePPPConnectScript;
  2026.  
  2027.     savePPPConnectScript = configurationDigest->fPPPConnectScript;
  2028.     OTMemzero(configurationDigest, sizeof(configurationDigest));
  2029.     configurationDigest->fPPPConnectScript = savePPPConnectScript;
  2030. }
  2031.  
  2032. static OSStatus BuildRemoteConfigurationDigestFromPackedPrefs(Handle packedPrefs,                                     
  2033.                                     NSHRemoteConfigurationDigest *configurationDigest)
  2034.     // This routine fills out a Remote Access configuration digest
  2035.     // based on the packed preferences.  The basic algorithm
  2036.     // is to iterate through all the preferences, looking at
  2037.     // each one and putting the data from that preference into
  2038.     // the configuration digest.
  2039. {
  2040.     OSStatus err;
  2041.     SInt8 s;
  2042.     ByteCount cookie;
  2043.     OSType prefType;
  2044.     ByteCount prefSize;
  2045.     void *prefData;
  2046.     
  2047.     MoreAssertQ(packedPrefs != nil);
  2048.     MoreAssertQ(configurationDigest != nil);
  2049.     
  2050.     ClearRemoteConfigurationDigest(configurationDigest);
  2051.  
  2052.     configurationDigest->fProtocol = kOTCfgTypeRemote;
  2053.     
  2054.     s = HGetState(packedPrefs);
  2055.     HLock(packedPrefs);
  2056.     
  2057.     err = noErr;
  2058.     cookie = 0;
  2059.     do {
  2060.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize);
  2061.         switch (prefType) {
  2062.             case 0:
  2063.                 // do nothing, this is the loop exit condition
  2064.                 break;
  2065.         
  2066.         // 'pnam'
  2067.         
  2068.             case kOTCfgUserVisibleNamePref:
  2069.                 if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) {
  2070.                     BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255));
  2071.                 } else {
  2072.                     err = -8;
  2073.                 }
  2074.                 break;
  2075.  
  2076.         // 'cmsc' -> fPPPConnectAutomatically
  2077.  
  2078.             case kOTCfgRemoteClientMiscPref:
  2079.                 if ( prefSize == 8 && RemoteVersionAcceptable(((UInt32 *) prefData)[0]) ) {
  2080.                     configurationDigest->fPPPConnectAutomatically = ((UInt32 *) prefData)[1];
  2081.                 } else {
  2082.                     err = -8;
  2083.                 }
  2084.                 break;
  2085.  
  2086.         // 'conn' -> fGuestLogin, fPasswordValid, fPhoneNumber, fFlashIconWhileConnected, fPromptToRemainConnected, fPromptInterval, fSerialProtocol, fPPPAllowModemCompression, fPPPConnectMode, fPPPConnectScriptName, fPPPConnectScript
  2087.             case kOTCfgRemoteConnectPref:
  2088.                 if ( prefSize == sizeof(OTCfgRemoteConnect) && RemoteVersionAcceptable(((UInt32 *) prefData)[0]) ) {
  2089.                     OTCfgRemoteConnect *configPtr;
  2090.                     
  2091.                     configPtr = (OTCfgRemoteConnect *) prefData;
  2092.                     
  2093.                     configurationDigest->fGuestLogin = configPtr->isGuest;
  2094.                     configurationDigest->fPasswordValid = configPtr->passwordSaved;
  2095.                     configurationDigest->fFlashIconWhileConnected = configPtr->flashConnectedIcon;
  2096.                     configurationDigest->fPromptToRemainConnected = configPtr->issueConnectedReminders;
  2097.                     configurationDigest->fPromptInterval = configPtr->reminderMinutes;
  2098.                     configurationDigest->fSerialProtocol = configPtr->serialProtocolMode;
  2099.                     configurationDigest->fPPPAllowModemCompression = configPtr->allowModemDataCompression;
  2100.                     configurationDigest->fPPPConnectMode = configPtr->chatMode;
  2101.                     BlockMoveData(configPtr->chatScriptName, configurationDigest->fPPPConnectScriptName, sizeof(Str63));
  2102.                 } else {
  2103.                     err = -8;
  2104.                 }
  2105.                 break;
  2106.  
  2107.         // 'cusr' -> fUserName
  2108.  
  2109.             case kOTCfgRemoteUserPref:
  2110.                 if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) {
  2111.                     BlockMoveData(prefData, configurationDigest->fUserName, sizeof(Str255));
  2112.                 } else {
  2113.                     err = -8;
  2114.                 }
  2115.                 break;
  2116.  
  2117.         // 'pass' -> fPassword
  2118.  
  2119.             case kOTCfgRemotePasswordPref:
  2120.                 if ( prefSize == sizeof(Str255) ) {
  2121.                     BlockMoveData(prefData, configurationDigest->fPassword, sizeof(Str255));
  2122.                     
  2123.                     // I deliberately didn't decode the password here.  If you want
  2124.                     // the decoded password, you have to do it yourself.
  2125.                     
  2126.                 } else {
  2127.                     err = -8;
  2128.                 }
  2129.                 break;
  2130.  
  2131.         // 'cdia' -> fRedialMode, fRedialTimes, fRedialDelay
  2132.  
  2133.             case kOTCfgRemoteDialingPref:
  2134.                 if ( prefSize == sizeof(OTCfgRemoteDialing)
  2135.                             && RemoteVersionAcceptable(((UInt32 *)prefData)[0])
  2136.                             && ((UInt32 *)prefData)[1] == 'dial') {
  2137.                     OTCfgRemoteDialing *dialingPtr;
  2138.  
  2139.                     dialingPtr = (OTCfgRemoteDialing *) prefData;
  2140.                     configurationDigest->fRedialMode = dialingPtr->dialMode;
  2141.                     configurationDigest->fRedialTimes = dialingPtr->redialTries;
  2142.                     configurationDigest->fRedialDelay = dialingPtr->redialDelay;
  2143.                 } else {
  2144.                     err = -8;
  2145.                 }
  2146.                 break;
  2147.  
  2148.         // 'ipcp' -> fDisconnectIfIdle, fDisconnectInterval, fPPPAllowTCPIPHeaderCompression
  2149.  
  2150.             case kOTCfgRemoteIPCPPref:
  2151.                 // Sometimes the second long of prefData has 'ipcp' in it, but
  2152.                 // we can't check that because often it doesn't!
  2153.                 if ( prefSize == sizeof(OTCfgRemoteIPCP) && RemoteVersionAcceptable(((UInt32 *)prefData)[0])) {
  2154.                     OTCfgRemoteIPCP *ipcpPtr;
  2155.                     
  2156.                     ipcpPtr = (OTCfgRemoteIPCP *) prefData;
  2157.                     configurationDigest->fDisconnectIfIdle = ipcpPtr->idleTimerEnabled;
  2158.                     configurationDigest->fDisconnectInterval = ipcpPtr->idleTimerMilliseconds;
  2159.                     configurationDigest->fPPPAllowTCPIPHeaderCompression = ipcpPtr->compressTCPHeaders;
  2160.                 } else {
  2161.                     err = -8;
  2162.                 }
  2163.                 break;
  2164.  
  2165.         // 'logo' -> fVerboseLogging
  2166.         
  2167.             case kOTCfgRemoteLogOptionsPref:
  2168.                 if ( prefSize == sizeof(OTCfgRemoteLogOptions)
  2169.                             && RemoteVersionAcceptable(((UInt32 *)prefData)[0])
  2170.                             && ((UInt32 *)prefData)[1] == 'lgop') {
  2171.                     OTCfgRemoteLogOptions *logPtr;
  2172.                     
  2173.                     logPtr = (OTCfgRemoteLogOptions *) prefData;
  2174.                     configurationDigest->fVerboseLogging = logPtr->logLevel;
  2175.                 } else {
  2176.                     err = -8;
  2177.                 }
  2178.                 break;
  2179.                 
  2180.         // 'cadr' -> fPhoneNumber
  2181.         
  2182.             case kOTCfgRemoteAddressPref:
  2183.                 if ( prefSize <= 255 ) {
  2184.                     configurationDigest->fPhoneNumber[0] = prefSize;
  2185.                     BlockMoveData(prefData, &configurationDigest->fPhoneNumber[1], prefSize);
  2186.                 } else {
  2187.                     err = -8;
  2188.                 }
  2189.                 break;
  2190.                 
  2191.         // 'cead' -> fAlternatePhoneNumber
  2192.         
  2193.             case kOTCfgRemoteAlternateAddressPref:
  2194.                 if ( prefSize == sizeof(Str255) + sizeof(UInt32)
  2195.                             && ((UInt32 *)prefData)[0] == 0) {
  2196.                     BlockMoveData( ((char *)prefData) + sizeof(UInt32), configurationDigest->fAlternatePhoneNumber, sizeof(Str255));
  2197.                 } else {
  2198.                     err = -8;
  2199.                 }
  2200.                 break;
  2201.                 
  2202.         // 'ccha' -> fPPPConnectScript
  2203.         
  2204.             case kOTCfgRemoteChatPref:
  2205.                 if ( configurationDigest->fPPPConnectScript != nil ) {
  2206.                     err = PtrToXHand(prefData, configurationDigest->fPPPConnectScript, prefSize);
  2207.                 }
  2208.                 break;
  2209.     
  2210.             case kOTCfgRemoteUserModePref:
  2211.             case kOTCfgRemoteClientLocksPref:
  2212.             case kOTCfgRemoteLCPPref:
  2213.             case kOTCfgRemoteARAPPref:
  2214.             case kOTCfgRemoteDialAssistPref:
  2215.             case kOTCfgRemoteX25Pref:
  2216.             case kOTCfgRemoteTerminalPref:
  2217.                 // Known preferences which we don't need to pay
  2218.                 // attention to but shouldn't cause the following
  2219.                 // assert to trigger.
  2220.                 break;
  2221.                 
  2222.             default:
  2223.                 // Unexpected preference type in the packed prefs.
  2224.                 // This is not super-fatal, but let the developer know in debug builds.
  2225.                 MoreAssertQ(false);
  2226.                 break;
  2227.         }
  2228.     } while (err == noErr && prefType != 0);
  2229.     
  2230.     HSetState(packedPrefs, s);
  2231.  
  2232.     // Fix up the redial preferences.  We should only return
  2233.     // an alternate redial phone number if the redial mode is
  2234.     // appropriate.  This is not strictly necessary, but it
  2235.     // tidies up one of the anomalies noticed by the test case.
  2236.     
  2237.     if (err == noErr) {
  2238.         if ( configurationDigest->fRedialMode != kOTCfgRemoteRedialMainAndAlternate ) {
  2239.             configurationDigest->fAlternatePhoneNumber[0] = 0;
  2240.         }
  2241.     }
  2242.  
  2243.     return err;
  2244. }
  2245.  
  2246. /////////////////////////////////////////////////////////////////
  2247. #pragma mark ----- Modem Packer/Unpacker ------
  2248.  
  2249. // These routines convert between the Modem configuration digest
  2250. // (a big record with all the relevant fields in it) and the
  2251. // packed preferences data (which is read from or written to the
  2252. // preferences store).  These routines are shared between the
  2253. // preferences database and preferences file implementations.
  2254.  
  2255. static OSStatus BuildPackedPrefsFromModemConfigurationDigest(
  2256.                                     const NSHModemConfigurationDigest *configurationDigest, 
  2257.                                     Boolean forceDefaults,
  2258.                                     Handle *packedPrefs)
  2259.     // This routine converts a Modem configuration digest
  2260.     // to a handle containing packed preferences.
  2261.     //
  2262.     // forceDefaults is not needed because the Modem digest
  2263.     // has no handle-based field.
  2264. {
  2265.     #pragma unused(forceDefaults)
  2266.     OSStatus err;
  2267.     OTPortRecord portRec;
  2268.     PrefBuilderState state;    
  2269.     UInt8 buffer[256];
  2270.     OTCfgModemLocks *locksPtr;
  2271.     OTCfgModemGeneral *modemConfig;
  2272.  
  2273.     MoreAssertQ(configurationDigest != nil);
  2274.     MoreAssertQ(packedPrefs != nil);
  2275.     
  2276.     *packedPrefs = nil;
  2277.     
  2278.     err = noErr;
  2279.     if ( ! OTFindPortByRef(&portRec, configurationDigest->fPortRef) ) {
  2280.         err = kOTNotFoundErr;
  2281.     }
  2282.     
  2283.     if (err == noErr) {
  2284.         BuilderNew(&state);
  2285.  
  2286.     // 'pnam'
  2287.     
  2288.         BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1);
  2289.  
  2290.     // 'ccl ' -> fPortRef, fModemScript, fDialToneMode, fSpeakerOn, fPulseDial, fHintPortName
  2291.     
  2292.         OTMemzero(buffer, sizeof(buffer));
  2293.         MoreAssertQ(sizeof(*modemConfig) <= sizeof(buffer));
  2294.         modemConfig = (OTCfgModemGeneral *) buffer;
  2295.         modemConfig->version = 0x00010000;
  2296.         modemConfig->useModemScript = true;
  2297.         modemConfig->modemScript = configurationDigest->fModemScript;
  2298.         modemConfig->modemSpeakerOn = configurationDigest->fSpeakerOn;
  2299.         modemConfig->modemPulseDial = configurationDigest->fPulseDial;
  2300.         modemConfig->modemDialToneMode = configurationDigest->fDialToneMode;
  2301.         BlockMoveData(portRec.fPortName, modemConfig->lowerLayerName, 36);
  2302.         BuilderNewPref(&state, kOTCfgModemGeneralPrefs, modemConfig, sizeof(*modemConfig));
  2303.         
  2304.     // 'lkmd' -> $00000001 + $00000000 * 4
  2305.     
  2306.         OTMemzero(buffer, sizeof(buffer));
  2307.         MoreAssertQ(sizeof(*locksPtr) <= sizeof(buffer));
  2308.         locksPtr = (OTCfgModemLocks *) buffer;
  2309.         locksPtr->version = 1;
  2310.         BuilderNewPref(&state, kOTCfgModemLocksPref, locksPtr, sizeof(*locksPtr));
  2311.         
  2312.     // 'mdpw' -> $00 * 256
  2313.  
  2314.         OTMemzero(buffer, sizeof(buffer));
  2315.         BuilderNewPref(&state, kOTCfgModemAdminPasswordPref, buffer, 256);
  2316.     
  2317.         err = BuilderDone(&state, packedPrefs);
  2318.     }
  2319.  
  2320.     return err;
  2321. }
  2322.  
  2323. static OSStatus BuildModemConfigurationDigestFromPackedPrefs(Handle packedPrefs,                                     
  2324.                                     NSHModemConfigurationDigest *configurationDigest)
  2325.     // This routine fills out a Modem configuration digest
  2326.     // based on the packed preferences.  The basic algorithm
  2327.     // is to iterate through all the preferences, looking at
  2328.     // each one and putting the data from that preference into
  2329.     // the configuration digest.  At the end it does some jiggery
  2330.     // pokery that's explained in the comment down there.
  2331. {
  2332.     OSStatus err;
  2333.     SInt8 s;
  2334.     ByteCount cookie;
  2335.     OSType prefType;
  2336.     ByteCount prefSize;
  2337.     void *prefData;
  2338.     OTPortRecord portRec;
  2339.     OTCfgModemGeneral *modemConfig;
  2340.     
  2341.     MoreAssertQ(packedPrefs != nil);
  2342.     MoreAssertQ(configurationDigest != nil);
  2343.     
  2344.     OTMemzero(configurationDigest, sizeof(configurationDigest));
  2345.  
  2346.     configurationDigest->fProtocol = kOTCfgTypeModem;
  2347.     
  2348.     s = HGetState(packedPrefs);
  2349.     HLock(packedPrefs);
  2350.     
  2351.     err = noErr;
  2352.     cookie = 0;
  2353.     do {
  2354.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize);
  2355.         switch (prefType) {
  2356.             case 0:
  2357.                 // do nothing, this is the loop exit condition
  2358.                 break;
  2359.         
  2360.         // 'pnam'
  2361.         
  2362.             case kOTCfgUserVisibleNamePref:
  2363.                 if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) {
  2364.                     BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255));
  2365.                 } else {
  2366.                     err = -8;
  2367.                 }
  2368.                 break;
  2369.         
  2370.         // 'ccl '
  2371.             case kOTCfgModemGeneralPrefs:
  2372.                 modemConfig = (OTCfgModemGeneral *) prefData;
  2373.                 if (prefSize == sizeof(OTCfgModemGeneral) && modemConfig->version == 0x00010000) {
  2374.                     if (modemConfig->useModemScript) {
  2375.                         configurationDigest->fModemScript = modemConfig->modemScript;
  2376.                     } else {
  2377.                         // leave fModemScript set to all zeros
  2378.                     }
  2379.                     configurationDigest->fSpeakerOn = modemConfig->modemSpeakerOn;
  2380.                     configurationDigest->fPulseDial = modemConfig->modemPulseDial;
  2381.                     configurationDigest->fDialToneMode = modemConfig->modemDialToneMode;
  2382.                     configurationDigest->fHintPortName[0] = OTStrLength( (char *) modemConfig->lowerLayerName);
  2383.                     MoreAssertQ(configurationDigest->fHintPortName[0] <= 63);
  2384.                     BlockMoveData(modemConfig->lowerLayerName, &configurationDigest->fHintPortName[1], configurationDigest->fHintPortName[0]);
  2385.                 } else {
  2386.                     err = -8;
  2387.                 }
  2388.  
  2389.                 break;
  2390.     
  2391.         // 'lkmd'
  2392.         
  2393.             case kOTCfgModemLocksPref:
  2394.                 // just check the size
  2395.                 if (prefSize != 20) {
  2396.                     err = -8;
  2397.                 }
  2398.                 break;
  2399.     
  2400.         // 'mdpw'
  2401.         
  2402.             case kOTCfgModemAdminPasswordPref:
  2403.                 // just check the size
  2404.                 if (prefSize != 256) {
  2405.                     err = -8;
  2406.                 }
  2407.                 break;
  2408.  
  2409.             default:
  2410.                 // Unexpected preference type in the packed prefs.
  2411.                 // This is not super-fatal, but let the developer know in debug builds.
  2412.                 MoreAssertQ(false);
  2413.                 break;
  2414.         }
  2415.     } while (err == noErr && prefType != 0);
  2416.     
  2417.     HSetState(packedPrefs, s);
  2418.  
  2419.     // Before we leave, we have to set up the fPortRef field of the
  2420.     // configuration digest.  This is much easier than it was for TCP/IP.
  2421.     // Basically, if we have a port that matches the port name stored
  2422.     // in the preferences, that's the one we're going to return.
  2423.     
  2424.     if (err == noErr) {
  2425.         // Put a null byte after the last byte of the hint.
  2426.         // We know we have the space because they're declared as
  2427.         // Str63s.  We need this to pass it as a C string to OTFindPort.
  2428.         
  2429.         configurationDigest->fHintPortName[configurationDigest->fHintPortName[0] + 1] = 0;
  2430.         
  2431.         if ( OTFindPort(&portRec, ((char *) configurationDigest->fHintPortName) + 1) ) {
  2432.             configurationDigest->fPortRef = portRec.fRef;
  2433.         } else {
  2434.             configurationDigest->fPortRef = kOTInvalidPortRef;
  2435.         }
  2436.     }
  2437.     
  2438.     return err;
  2439. }
  2440.  
  2441. /////////////////////////////////////////////////////////////////
  2442. #pragma mark ----- Packer/Unpacker Dispatch ------
  2443.  
  2444. static OSStatus BuildConfigurationDigestFromPackedPrefs(OSType protocol, Handle packedPrefs,
  2445.                                                 NSHConfigurationDigest *configurationDigest)
  2446.     // A simple dispatcher that calls the appropriate protocol-specific
  2447.     // BuildXxxConfigurationDigestFromPackedPrefs routine based on the supplied
  2448.     // protocol.
  2449. {
  2450.     OSStatus err;
  2451.     
  2452.     switch (protocol) {
  2453.         case kOTCfgTypeTCPv4:
  2454.             err = BuildTCPv4ConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fTCPv4);
  2455.             break;
  2456.         case kOTCfgTypeRemote:
  2457.             err = BuildRemoteConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fRemote);
  2458.             break;
  2459.         case kOTCfgTypeModem:
  2460.             err = BuildModemConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fModem);
  2461.             break;
  2462.         default:
  2463.             err = paramErr;
  2464.             break;
  2465.     }
  2466.     return err;
  2467. }
  2468.  
  2469. static OSStatus BuildPackedPrefsFromConfigurationDigest(const NSHConfigurationDigest *configurationDigest, Boolean forceDefaults, Handle *packedPrefs)
  2470.     // A simple dispatcher that calls the appropriate protocol-specific
  2471.     // BuildPackedPrefsFromXxxConfigurationDigest routine based on the
  2472.     // protocol in the digest.
  2473. {
  2474.     OSStatus err;
  2475.     
  2476.     switch (configurationDigest->fCommon.fProtocol) {
  2477.         case kOTCfgTypeTCPv4:
  2478.             err = BuildPackedPrefsFromTCPv4ConfigurationDigest(&configurationDigest->fTCPv4, forceDefaults, packedPrefs);
  2479.             break;
  2480.         case kOTCfgTypeRemote:
  2481.             err = BuildPackedPrefsFromRemoteConfigurationDigest(&configurationDigest->fRemote, forceDefaults, packedPrefs);
  2482.             break;
  2483.         case kOTCfgTypeModem:
  2484.             err = BuildPackedPrefsFromModemConfigurationDigest(&configurationDigest->fModem, forceDefaults, packedPrefs);
  2485.             break;
  2486.         default:
  2487.             err = paramErr;
  2488.             break;
  2489.     }
  2490.     return err;
  2491. }
  2492.  
  2493. /////////////////////////////////////////////////////////////////
  2494. #pragma mark ----- Internet Setup using Database ------
  2495.  
  2496. static OSStatus WritePackedPrefsToDatabase(const MNSDatabaseRef *ref,
  2497.                             const CfgEntityRef *configEntity,
  2498.                             Handle packedPrefs)
  2499.     // This is a utility routine that simply writes the entire
  2500.     // set of packed preferences to the configuration database
  2501.     // entity specified by ref and configEntity.
  2502. {
  2503.     SInt8 s;
  2504.     OSStatus err;
  2505.     ByteCount cookie;
  2506.     OSType prefType;
  2507.     ByteCount prefSize;
  2508.     void *prefPtr;
  2509.     
  2510.     MoreAssertQ(ref != nil);
  2511.     MoreAssertQ(configEntity != nil);
  2512.     MoreAssertQ(packedPrefs != nil);
  2513.     
  2514.     // Lock down the packed preferences.
  2515.     
  2516.     s = HGetState(packedPrefs);
  2517.     HLock(packedPrefs);
  2518.     MoreAssertQ(MemError() == noErr);
  2519.  
  2520.     // Iterate through each preference, write it out to the database.
  2521.     
  2522.     err = noErr;    
  2523.     cookie = 0;
  2524.     do {
  2525.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefPtr, &prefSize);
  2526.         if (prefType != 0) {
  2527.             err = MNSSetPref(ref, configEntity, prefType, prefPtr, prefSize);
  2528.         }
  2529.     } while (err == noErr && prefType != 0);
  2530.     
  2531.     // Clean up.
  2532.     
  2533.     HSetState(packedPrefs, s);
  2534.     
  2535.     return err;
  2536. }
  2537.  
  2538. static pascal void BuilderIterator(OSType prefType, void *prefData, ByteCount prefSize, void *refcon)
  2539.     // This is an MNSIterateEntity callback routine.  It simply
  2540.     // adds each preference in the entity to the the builder
  2541.     // whose state is passed in as the refcon parameter.  The upshot
  2542.     // is that the entity's entire preference set is added to
  2543.     // the packed prefs.  I love it when concepts like builders
  2544.     // and iterators work together (-:
  2545. {
  2546.     switch (prefType) {
  2547.         case kOTCfgCompatResourceIDPref:
  2548.         case kOTCfgCompatResourceNamePref:
  2549.         case kOTCfgCompatNamePref:
  2550.             // Do nothing.  These preferences are used purely by
  2551.             // Network Setup to maintain accurate roundtrip
  2552.             // conversions between the backward compatibility
  2553.             // files and the database.  We never need the
  2554.             // information from the files, so we can just 
  2555.             // ignore them completely.
  2556.             break;
  2557.         default:
  2558.             BuilderNewPref( (PrefBuilderState *) refcon, prefType, prefData, prefSize);
  2559.             break;
  2560.     }
  2561. }
  2562.  
  2563. static OSStatus ReadPackedPrefsFromDatabase(const MNSDatabaseRef *ref,
  2564.                             const CfgEntityRef *configEntity,
  2565.                             Handle *packedPrefs)
  2566.     // This routine builds a packed preference handle based on
  2567.     // all the preferences in the entity specified by ref and
  2568.     // configEntity.  *packedPrefs must be nil before you call
  2569.     // the routine.  The routine either succeeds (returning noErr
  2570.     // and setting *packedPrefs to a valid handle), or fails
  2571.     // (returning an error, and leaving *packedRefs as nil).
  2572. {
  2573.     OSStatus err;
  2574.     OSStatus err2;
  2575.     PrefBuilderState state;
  2576.  
  2577.     MoreAssertQ(ref != nil);
  2578.     MoreAssertQ(configEntity != nil);
  2579.     MoreAssertQ(packedPrefs != nil);
  2580.     MoreAssertQ(*packedPrefs == nil);
  2581.  
  2582.     BuilderNew(&state);
  2583.     err = MNSIterateEntity(ref, configEntity, BuilderIterator, &state);
  2584.  
  2585.     err2 = BuilderDone(&state, packedPrefs);
  2586.     if (err == noErr) {
  2587.         err = err2;
  2588.     }
  2589.     MoreAssertQ(err != noErr && packedPrefs == nil || err == noErr && packedPrefs != nil);
  2590.     return err;
  2591. }
  2592.  
  2593. static void FillOutConfigBits(OSType protocol, ConstStr255Param name, NSHConfigurationEntry *thisConfig)
  2594.     // Fills out the name, selected and cookie3 fields of thisConfig.
  2595.     // This is basically common code extracted from CreateConfigurationDatabase
  2596.     // and DuplicateConfigurationDatabase.
  2597. {
  2598.     MoreAssertQ(thisConfig != nil);
  2599.  
  2600.     BlockMoveData(name, thisConfig->name, sizeof(Str255));
  2601.     thisConfig->selected = false;
  2602.     thisConfig->cookie = 0;
  2603.     thisConfig->cookie3.fClass = kOTCfgClassNetworkConnection;
  2604.     thisConfig->cookie3.fType  = protocol;
  2605.     BlockMoveData(name, thisConfig->cookie3.fName, sizeof(Str255));
  2606.     thisConfig->cookie3.fIcon.fFile.vRefNum = 0;
  2607.     thisConfig->cookie3.fIcon.fFile.parID = 0;
  2608.     thisConfig->cookie3.fIcon.fFile.name[0] = 0;
  2609.     thisConfig->cookie3.fIcon.fResID = 0;
  2610.     thisConfig->cookie4 = protocol;
  2611. }
  2612.  
  2613. static OSStatus CreateConfigurationDatabase(const NSHConfigurationDigest *configurationDigest,
  2614.                                                 NSHConfigurationEntry *createdConfig)
  2615.     // Implementation of NSHCreateConfiguration which uses the Network Setup
  2616.     // database.  See NSHCreateConfiguration's comment in header
  2617.     // file for interface specification.
  2618.     //
  2619.     // This routine opens up the database, creates an entity, and sets
  2620.     // the contents of the entity from the digest using common utility
  2621.     // routines.  Plus there's a bunch of housekeeping that's documented
  2622.     // by comments in the routine.
  2623. {
  2624.     OSStatus err;
  2625.     OSStatus err2;
  2626.     NSHConfigurationEntry tmpConfig;
  2627.     NSHConfigurationEntry *destConfig;
  2628.     MNSDatabaseRef ref;
  2629.     CfgAreaID originalArea;
  2630.     Handle packedPrefs;
  2631.     
  2632.     MoreAssertQ(configurationDigest != nil);
  2633.  
  2634.     // createdConfig is an optional parameter.  To simplify our
  2635.     // code, we use a temporary config if the user didn't supply it.
  2636.  
  2637.     if (createdConfig != nil) {
  2638.         destConfig = createdConfig;
  2639.     } else {
  2640.         destConfig = &tmpConfig;
  2641.     }
  2642.  
  2643.     packedPrefs = nil;
  2644.     
  2645.     err = MNSOpenDatabase(&ref, true);
  2646.     if (err == noErr) {
  2647.         FillOutConfigBits(configurationDigest->fCommon.fProtocol, configurationDigest->fCommon.fConfigName, destConfig);
  2648.         
  2649.         // Create the entity, getting back the CfgEntityRef.
  2650.         
  2651.         err = OTCfgCreateEntity(ref.dbRef, ref.area, &destConfig->cookie3,
  2652.                                 &destConfig->cookie2);
  2653.         if (err == noErr) {
  2654.         
  2655.             // Fill out the entity with the information from the digest.
  2656.             
  2657.             err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, true, &packedPrefs);
  2658.             if (err == noErr) {
  2659.                 err = WritePackedPrefsToDatabase(&ref, &destConfig->cookie2, packedPrefs);
  2660.             }
  2661.  
  2662.             if (err != noErr) {
  2663.                 // There is no need to delete the half-created config
  2664.                 // because we're about to abort the database transaction,
  2665.                 // which will discard any changes we made.
  2666.             }
  2667.         }
  2668.  
  2669.         originalArea = ref.originalArea;
  2670.         
  2671.         err2 = MNSCloseDatabase(&ref, err == noErr);
  2672.         if (err == noErr) {
  2673.             err = err2;
  2674.         }
  2675.         
  2676.         // The entity was created in a temporary area.  After we close
  2677.         // that area, that temporary entity goes away, being replaced by
  2678.         // the final entity in the real area.  To avoid client confusion,
  2679.         // we set the returned entity's area to that original area.
  2680.         // Otherwise, when they pass this returned entity to 
  2681.         // the get or set routine, Network Setup will complain that
  2682.         // the entity's area doesn't exist.
  2683.         
  2684.         if (err == noErr) {
  2685.             destConfig->cookie2.fLoc = originalArea;
  2686.         }
  2687.     }
  2688.  
  2689.     // Clean up.
  2690.     
  2691.     if (packedPrefs != nil) {
  2692.         DisposeHandle(packedPrefs);
  2693.         MoreAssertQ(MemError() == noErr);
  2694.     }
  2695.     
  2696.     return err;
  2697. }
  2698.  
  2699. static OSStatus DuplicateConfigurationDatabase(const NSHConfigurationEntry *config,
  2700.                                                 ConstStr255Param newConfigName,
  2701.                                                 NSHConfigurationEntry *createdConfig)
  2702.     // Implementation of NSHDuplicateConfiguration which uses the Network Setup
  2703.     // database.  See NSHDuplicateConfiguration's comment in header
  2704.     // file for interface specification.
  2705.     //
  2706.     // This routine opens up the database, creates an entity, and sets
  2707.     // the contents of the entity by duplicating the entity referenced
  2708.     // by config.  And then there's a bunch of housekeeping.
  2709. {
  2710.     OSStatus err;
  2711.     OSStatus err2;
  2712.     NSHConfigurationEntry tmpConfig;
  2713.     NSHConfigurationEntry *destConfig;
  2714.     MNSDatabaseRef ref;
  2715.     CfgAreaID originalArea;
  2716.     
  2717.     MoreAssertQ(config != nil);
  2718.     MoreAssertQ(newConfigName != nil);
  2719.  
  2720.     // createdConfig is an optional parameter.  To simplify our
  2721.     // code, we use a temporary config if the user didn't supply it.
  2722.  
  2723.     if (createdConfig != nil) {
  2724.         destConfig = createdConfig;
  2725.     } else {
  2726.         destConfig = &tmpConfig;
  2727.     }
  2728.     
  2729.     err = MNSOpenDatabase(&ref, true);
  2730.     if (err == noErr) {
  2731.         FillOutConfigBits(config->cookie3.fType, newConfigName, destConfig);
  2732.  
  2733.         // Create the entity, getting back the CfgEntityRef.
  2734.         
  2735.         err = OTCfgCreateEntity(ref.dbRef, ref.area, &destConfig->cookie3,
  2736.                                 &destConfig->cookie2);
  2737.         if (err == noErr) {
  2738.             err = OTCfgDuplicateEntity(ref.dbRef, &config->cookie2, &destConfig->cookie2);
  2739.             if (err == noErr) {
  2740.                 // Force the user-visible name to newConfigName.
  2741.                 err = MNSSetPref(&ref, &destConfig->cookie2, kOTCfgUserVisibleNamePref, newConfigName, *newConfigName + 1);
  2742.             }
  2743.  
  2744.             if (err != noErr) {
  2745.                 // There is no need to delete the half-created config
  2746.                 // because we're about to abort the database transaction,
  2747.                 // which will discard any changes we made.
  2748.             }
  2749.         }
  2750.  
  2751.         originalArea = ref.originalArea;
  2752.         
  2753.         err2 = MNSCloseDatabase(&ref, err == noErr);
  2754.         if (err == noErr) {
  2755.             err = err2;
  2756.         }
  2757.         
  2758.         // The entity was created in a temporary area.  After we close
  2759.         // that area, that temporary entity goes away, being replaced by
  2760.         // the final entity in the real area.  To avoid client confusion,
  2761.         // we set the returned entity's area to that original area.
  2762.         // Otherwise, when they pass this returned entity to 
  2763.         // the get or set routine, Network Setup will complain that
  2764.         // the entity's area doesn't exist.
  2765.         
  2766.         if (err == noErr) {
  2767.             destConfig->cookie2.fLoc = originalArea;
  2768.         }
  2769.     }
  2770.     
  2771.     return err;
  2772. }
  2773.  
  2774. static OSStatus GetConfigurationDatabase(const NSHConfigurationEntry *config,
  2775.                                                 NSHConfigurationDigest *configurationDigest)
  2776.     // Implementation of NSHGetConfiguration which uses the Network Setup
  2777.     // database.  See NSHGetConfiguration's comment in header
  2778.     // file for interface specification.
  2779.     //
  2780.     // This routine opens up the database, reads the preferences out of
  2781.     // the configuration, and then converts them to a configuration digest.
  2782. {
  2783.     OSStatus err;
  2784.     OSStatus err2;
  2785.     MNSDatabaseRef ref;
  2786.     Handle packedPrefs;
  2787.     
  2788.     MoreAssertQ(config != nil);
  2789.     MoreAssertQ(configurationDigest != nil);
  2790.     
  2791.     packedPrefs = nil;
  2792.     
  2793.     err = MNSOpenDatabase(&ref, false);
  2794.     if (err == noErr) {
  2795.         err = ReadPackedPrefsFromDatabase(&ref, &config->cookie2, &packedPrefs);
  2796.         if (err == noErr) {
  2797.             err = BuildConfigurationDigestFromPackedPrefs(config->cookie4, packedPrefs, configurationDigest);
  2798.         }
  2799.         
  2800.         err2 = MNSCloseDatabase(&ref, false);
  2801.         if (err == noErr) {
  2802.             err = err2;
  2803.         }
  2804.     }
  2805.  
  2806.     if (packedPrefs != nil) {
  2807.         DisposeHandle(packedPrefs);
  2808.         MoreAssertQ(MemError() == noErr);
  2809.     }
  2810.     
  2811.     return err;
  2812. }
  2813.  
  2814. static void FixClientEntityForWriting(const MNSDatabaseRef *ref, const CfgEntityRef *clientEntity,
  2815.                                     CfgEntityRef *fixedEntity)
  2816.     // Entitys, as we expose them to the outside world, always
  2817.     // refer to the read area of the database.  But if we've opened
  2818.     // this area for writing, we're actually supposed to write to
  2819.     // a temporary writable area.  But the incoming entity references
  2820.     // from the client refer to the read area.  This routine
  2821.     // creates a fixed entity which is entirely based on the
  2822.     // incoming client entity except that it refers to the write
  2823.     // area we've just opened.
  2824. {
  2825.     MoreAssertQ(clientEntity->fLoc == ref->originalArea);
  2826.     *fixedEntity = *clientEntity;
  2827.     fixedEntity->fLoc = ref->area;
  2828. }
  2829.  
  2830. static OSStatus SetConfigurationDatabase(const NSHConfigurationEntry *config,
  2831.                                                 const NSHConfigurationDigest *configurationDigest)
  2832.     // Implementation of NSHSetConfiguration which uses the Network Setup
  2833.     // database.  See NSHSetConfiguration's comment in header
  2834.     // file for interface specification.
  2835.     //
  2836.     // This routine opens up the database and then sets the configuration
  2837.     // from the digest using using common utility routines
  2838. {
  2839.     OSStatus err;
  2840.     OSStatus err2;
  2841.     MNSDatabaseRef ref;
  2842.     CfgEntityRef tmpEntity;
  2843.     Handle packedPrefs;
  2844.  
  2845.     MoreAssertQ(config != nil);
  2846.     MoreAssertQ(configurationDigest != nil);
  2847.  
  2848.     packedPrefs = nil;
  2849.     
  2850.     err = MNSOpenDatabase(&ref, true);
  2851.     if (err == noErr) {
  2852.         FixClientEntityForWriting(&ref, &config->cookie2, &tmpEntity);
  2853.         err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, false, &packedPrefs);
  2854.         if (err == noErr) {
  2855.             err = WritePackedPrefsToDatabase(&ref, &tmpEntity, packedPrefs);
  2856.         }
  2857.  
  2858.         err2 = MNSCloseDatabase(&ref, err == noErr);
  2859.         if (err == noErr) {
  2860.             err = err2;
  2861.         }
  2862.     }
  2863.     
  2864.     // Clean up.
  2865.     
  2866.     if (packedPrefs != nil) {
  2867.         DisposeHandle(packedPrefs);
  2868.         MoreAssertQ(MemError() == noErr);
  2869.     }
  2870.  
  2871.     return err;
  2872. }
  2873.  
  2874. // The DeleteConfigFromSetParam data structure is used
  2875. // to pass parameters to DeleteConfigFromSetIterator.
  2876.  
  2877. enum {
  2878.     kDeleteConfigFromSetParamSignature = 'DcSp'
  2879. };
  2880.  
  2881. struct DeleteConfigFromSetParam {
  2882.     OSType        signature;                // is always kDeleteConfigFromSetParamSignature, debugging check 
  2883.     CfgEntityRef  *entityWereDeleting;
  2884.     CfgSetsVector **newSetsVector;
  2885.     OSStatus      latchedError;
  2886.     Boolean          dirty;
  2887.     Boolean          newSetContainsAppleTalk;
  2888.     Boolean          newSetContainsTCPv4;
  2889.     Boolean          newSetContainsRemote;
  2890.     Boolean          newSetContainsModem;
  2891.     Boolean          newSetContainsInfrared;
  2892. };
  2893. typedef struct DeleteConfigFromSetParam DeleteConfigFromSetParam;
  2894.  
  2895. static void InitDeleteConfigFromSetParam(DeleteConfigFromSetParam *param, CfgEntityRef *entityToDelete, CfgSetsVector **newSetsVector)
  2896.     // This routine initialises the DeleteConfigFromSetParam
  2897.     // data structure.
  2898. {
  2899.     param->signature = kDeleteConfigFromSetParamSignature;
  2900.     param->entityWereDeleting = entityToDelete;
  2901.     param->newSetsVector = newSetsVector;
  2902.     param->latchedError = noErr;
  2903.     param->dirty = false;
  2904.     param->newSetContainsAppleTalk     = false;
  2905.     param->newSetContainsTCPv4         = false;
  2906.     param->newSetContainsRemote     = false;
  2907.     param->newSetContainsModem         = false;
  2908.     param->newSetContainsInfrared     = false;
  2909. }
  2910.  
  2911. static pascal void DeleteConfigFromSetIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p)
  2912.     // This routine is an iterator passed to MNSIterateSet.  MNSIterateSet
  2913.     // repeatedly calls this routine, once for each element of the set.
  2914.     // The purpose of the routine is to:
  2915.     //
  2916.     // a) build a new set which contains all of the entities
  2917.     //    in the existing set except for the one we're deleting, and
  2918.     // b) ensure that the new set has an entity for each of the various
  2919.     //    backward compatibility  protocols.  If it does, we set the
  2920.     //    appropriate Boolean flag in the DeleteConfigFromSetParam passed
  2921.     //    in as the "p" parameter.
  2922.     //
  2923.     //    The reason why we need to do this is explained in the comment
  2924.     //      for DeleteConfigurationDatabase.
  2925. {
  2926.     OSStatus err;
  2927.     DeleteConfigFromSetParam *param;
  2928.     #pragma unused(ref)
  2929.     
  2930.     param = (DeleteConfigFromSetParam *) p;
  2931.     MoreAssertQ(param != nil);
  2932.     MoreAssertQ(param->signature == kDeleteConfigFromSetParamSignature);
  2933.     
  2934.     if (param->latchedError == noErr) {
  2935.         if ( ! OTCfgIsSameEntityRef(&thisElement->fEntityRef, param->entityWereDeleting, kOTCfgIgnoreArea) ) {
  2936.             // This entity isn't the one we're deleting, so copy it across to
  2937.             // newSetsVector and, if it's an entity for a protocol which has
  2938.             // a legacy prefs file, mark it as existing in the new set.
  2939.             
  2940.             err = PtrAndHand(thisElement, (Handle) param->newSetsVector, sizeof(*thisElement));
  2941.             if (err == noErr) {
  2942.                 (**(param->newSetsVector)).fCount += 1;
  2943.                 switch (thisElement->fEntityInfo.fType) {
  2944.                     case kOTCfgTypeAppleTalk:     param->newSetContainsAppleTalk     = true; break;
  2945.                     case kOTCfgTypeTCPv4:         param->newSetContainsTCPv4         = true; break;
  2946.                     case kOTCfgTypeRemote:         param->newSetContainsRemote     = true; break;
  2947.                     case kOTCfgTypeModem:         param->newSetContainsModem         = true; break;
  2948.                     case kOTCfgTypeInfrared:     param->newSetContainsInfrared     = true; break;
  2949.                 }
  2950.             }
  2951.             param->latchedError = err;
  2952.         } else {
  2953.             // This entity is the one we're deleting, so don't copy it, and mark
  2954.             // the newSetsVector as dirty so that we write it back.
  2955.             param->dirty = true;
  2956.         }
  2957.     }
  2958. }
  2959.  
  2960. static OSStatus DeleteConfigurationDatabase(const NSHConfigurationEntry *config)
  2961.     // Implementation of NSHDeleteConfiguration which uses the Network Setup
  2962.     // database.  See NSHDeleteConfiguration's comment in header
  2963.     // file for interface specification.
  2964.     //
  2965.     // At first glance, this should be easy, ie a straight pass through to
  2966.     // OTCfgDeleteEntity.  But then you have to worry about sets.  If any
  2967.     // set contains a reference to this entity, you have to delete that
  2968.     // reference out of the set.  And then you have to worry about
  2969.     // backward compatibility.  What happens if you delete the TCP/IP
  2970.     // configuration out of the active set?  When Network Setup goes
  2971.     // to export the database to the legacy preferences file, what
  2972.     // does it do if there's no active TCP/IP configuration.  The answer:
  2973.     // it messes up horribly.  So we prevent you from deleting an entity
  2974.     // if it's the last entity for a backward compatibility protocol.
  2975.     //
  2976.     // [Should this continue "... in the active set."  I don't think
  2977.     // so, because we want to allow folks to switch sets easily, and having
  2978.     // them worry about "Oh, this set doesn't have a Remote Access entity
  2979.     // so I can't switch to it." is asking too much.]
  2980. {
  2981.     OSStatus err;
  2982.     OSStatus err2;
  2983.     MNSDatabaseRef ref;
  2984.     CfgEntityRef entityToDelete;
  2985.     ItemCount entityCount;
  2986.     CfgEntityRef *entityRefs;
  2987.     ItemCount entityIndex;
  2988.     CfgSetsVector **newSetsVector;
  2989.     DeleteConfigFromSetParam param;
  2990.     
  2991.     MoreAssertQ(config != nil);
  2992.     
  2993.     entityRefs = nil;
  2994.     newSetsVector = (CfgSetsVector **) NewHandle(sizeof(UInt32));
  2995.     err = MemError();
  2996.  
  2997.     if (err == noErr) {
  2998.         err = MNSOpenDatabase(&ref, true);
  2999.     }
  3000.     if (err == noErr) {
  3001.         FixClientEntityForWriting(&ref, &config->cookie2, &entityToDelete);
  3002.  
  3003.         // Cool.  I don't have to worry about which order to delete
  3004.         // in (ie should I delete the entity or modify the set vectors
  3005.         // first) and how I roll back the changes in the case of a failure,
  3006.         // because changes are committed atomically by MNSCloseDatabase.
  3007.  
  3008.         // Delete the entity...
  3009.         
  3010.         if (err == noErr) {
  3011.             err = OTCfgDeleteEntity(ref.dbRef, &entityToDelete);
  3012.         }
  3013.         
  3014.         // Now remove it from any sets.  First get the list of sets...
  3015.         
  3016.         if (err == noErr) {
  3017.             err = MNSGetEntitiesList(&ref,
  3018.                                     kOTCfgClassSetOfSettings, kOTCfgTypeSetOfSettings,
  3019.                                     &entityCount,
  3020.                                     &entityRefs, nil);
  3021.         }
  3022.         
  3023.         if (err == noErr) {
  3024.         
  3025.             // Then, for each set entity we found...
  3026.  
  3027.             for (entityIndex = 0; entityIndex < entityCount; entityIndex++) {
  3028.                 
  3029.                 // Set up newSetsVector to be an empty CfgSetsVector,
  3030.                 // ie a handle with a 4 byte fCount field which is set
  3031.                 // to 0.
  3032.                 
  3033.                 SetHandleSize( (Handle) newSetsVector, sizeof(UInt32));
  3034.                 err = MemError();
  3035.                 if (err == noErr) {
  3036.                     (**newSetsVector).fCount = 0;
  3037.  
  3038.                     // Fill out param, which is a parameter block containing
  3039.                     // all the information that DeleteConfigFromSetIterator needs.
  3040.                     
  3041.                     InitDeleteConfigFromSetParam(¶m, &entityToDelete, newSetsVector);
  3042.                     
  3043.                     // Call DeleteConfigFromSetIterator on each element of
  3044.                     // the entityIndex'th set entity.
  3045.                     
  3046.                     err = MNSIterateSet(&ref,
  3047.                                         &entityRefs[entityIndex],
  3048.                                         DeleteConfigFromSetIterator, ¶m,
  3049.                                         false);
  3050.                     if (err == noErr) {
  3051.                         err = param.latchedError;
  3052.                     }
  3053.                 }
  3054.                 if (err == noErr && param.dirty) {
  3055.                     if (param.newSetContainsAppleTalk
  3056.                           && param.newSetContainsTCPv4
  3057.                           && param.newSetContainsRemote
  3058.                           && param.newSetContainsModem  
  3059.                           && param.newSetContainsInfrared) {
  3060.                         err = MNSSetPrefHandle(&ref, &entityRefs[entityIndex], kOTCfgSetsVectorPref, (Handle) newSetsVector);
  3061.                     } else {
  3062.                         err = -11;
  3063.                     }
  3064.                 }
  3065.                 if (err != noErr) {
  3066.                     break;
  3067.                 }
  3068.             }
  3069.         }
  3070.  
  3071.         err2 = MNSCloseDatabase(&ref, err == noErr);
  3072.         if (err == noErr) {
  3073.             err = err2;
  3074.         }
  3075.     }
  3076.     
  3077.     // Clean up.
  3078.     
  3079.     if (newSetsVector != nil) {
  3080.         DisposeHandle( (Handle) newSetsVector );
  3081.         MoreAssertQ(MemError() == noErr);
  3082.     }
  3083.     if (entityRefs != nil) {
  3084.         DisposePtr( (Ptr) entityRefs );
  3085.         MoreAssertQ(MemError() == noErr);
  3086.     }
  3087.     
  3088.     return err;
  3089. }
  3090.  
  3091. static OSStatus RenameConfigurationDatabase(const NSHConfigurationEntry *config,
  3092.                                               ConstStr255Param newConfigName,
  3093.                                               NSHConfigurationEntry *newConfig)
  3094.     // Implementation of NSHRenameConfiguration which uses the Network Setup
  3095.     // database.  See NSHRenameConfiguration's comment in header
  3096.     // file for interface specification.
  3097.     //
  3098.     // A relatively simple pass through to OTCfgSetEntityName.
  3099. {
  3100.     OSStatus err;
  3101.     OSStatus err2;
  3102.     NSHConfigurationEntry tmpConfig;
  3103.     NSHConfigurationEntry *destConfig;
  3104.     MNSDatabaseRef ref;
  3105.     CfgEntityRef tmpEntity;
  3106.  
  3107.     MoreAssertQ(config != nil);
  3108.     MoreAssertQ(newConfigName != nil);
  3109.  
  3110.     if (newConfig != nil) {
  3111.         destConfig = newConfig;
  3112.     } else {
  3113.         destConfig = &tmpConfig;
  3114.     }
  3115.  
  3116.     err = MNSOpenDatabase(&ref, true);
  3117.     if (err == noErr) {
  3118.         FillOutConfigBits(config->cookie3.fType, newConfigName, destConfig);
  3119.  
  3120.         FixClientEntityForWriting(&ref, &config->cookie2, &tmpEntity);
  3121.         err = OTCfgSetEntityName(ref.dbRef, &tmpEntity, newConfigName, &destConfig->cookie2);
  3122.  
  3123.         err2 = MNSCloseDatabase(&ref, err == noErr);
  3124.         if (err == noErr) {
  3125.             err = err2;
  3126.         }
  3127.     }
  3128.     return err;
  3129. }
  3130.  
  3131. /////////////////////////////////////////////////////////////////
  3132. #pragma mark ----- Internet Setup using File ------
  3133.  
  3134. static OSStatus ReadPackedPrefsFromFile(SInt16 configID, Handle *packedPrefs)
  3135.     // This routine builds a packed preference handle based on
  3136.     // all the preferences in the specified configuration of the
  3137.     // current resource file. *packedPrefs must be nil before you call
  3138.     // the routine.  The routine either succeeds (returning noErr
  3139.     // and setting *packedPrefs to a valid handle), or fails
  3140.     // (returning an error, and leaving *packedRefs as nil).
  3141. {
  3142.     OSStatus err;
  3143.     OSStatus err2;
  3144.     PrefBuilderState state;
  3145.     SInt16 typeCount;
  3146.     SInt16 typeIndex;
  3147.     OSType thisType;
  3148.     Handle thisResourceH;
  3149.     SInt16 junkID;
  3150.     OSType junkType;
  3151.     Str255 configName;
  3152.  
  3153.     MoreAssertQ(packedPrefs != nil);
  3154.     MoreAssertQ(*packedPrefs == nil);
  3155.  
  3156.     BuilderNew(&state);
  3157.  
  3158.     // Iterate through all the resource types in the file.
  3159.     // For each type, check to see whether there's a resource
  3160.     // with ID equal to configID in the file.  If there is,
  3161.     // add it to the packedPrefs.
  3162.     
  3163.     typeCount = Count1Types();
  3164.     err = ResError();
  3165.     if (err == noErr) {
  3166.         for (typeIndex = 1; typeIndex <= typeCount; typeIndex++) {
  3167.             Get1IndType(&thisType, typeIndex);
  3168.             err = ResError();
  3169.             if (err == noErr) {
  3170.                 thisResourceH = Get1Resource(thisType, configID);
  3171.                 err = CheckResError(thisResourceH);
  3172.                 if (err == noErr) {
  3173.                     HLock(thisResourceH);
  3174.                     
  3175.                     // We have to handle the 'cnam' as a special case.  In
  3176.                     // legacy files, the user-visible name of the preference
  3177.                     // is stored in the resource name of the 'cnam' preference.
  3178.                     // The preference data contains no useful information.
  3179.                     // So when we encounter a 'cnam' preference, we extract
  3180.                     // the resource name and write that to the packed prefs
  3181.                     // as a 'pnam' preference.  This is where the various
  3182.                     // digest routines expect the user-visible name to be.
  3183.                     // In addition, WritePackedPrefsToFile is smart enough to
  3184.                     // undo this.
  3185.                     
  3186.                     if (thisType == kOTCfgCompatNamePref) {
  3187.                         GetResInfo(thisResourceH, &junkID, &junkType, configName);
  3188.                         BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configName, configName[0] + 1);
  3189.                     } else {
  3190.                         BuilderNewPref(&state, thisType, *thisResourceH, GetHandleSize(thisResourceH));
  3191.                     }
  3192.                     
  3193.                     // ••• Gotcha •••
  3194.                     // Release the resource we just got.  This was a contentious
  3195.                     // issue when coding this.  We want to release the resource because 
  3196.                     // it avoids the resources building up in the memory of the client
  3197.                     // application.  But what happens if the resource file
  3198.                     // is being used by other code (ie someone else in this
  3199.                     // process has the file open read/write, so we're sharing
  3200.                     // a resource map with them) and we release the resource
  3201.                     // out from underneath them.  The answer is, of course,
  3202.                     // "bad things", but we have no way of detecting this condition.
  3203.                     // I'm just making the assumption that this situation won't
  3204.                     // happen.  As it is, it's fairly unlikely.  Most software
  3205.                     // that modifies preferences (most importantly the network 
  3206.                     // control panels) runs as a separate process, so resource
  3207.                     // map sharing is unlikely.  It could happen if you're running
  3208.                     // an extension inside the context of one of those applications,
  3209.                     // but doing that is asking for trouble anyway (-:
  3210.                     //
  3211.                     // For more background on this problem. see the comment about
  3212.                     // fsRdWrPerm in OpenNetworkPrefFile.
  3213.                     //
  3214.                     // One added advantage to releasing the resource is that we
  3215.                     // can simply HLock the handle above, rather than using the
  3216.                     // state HGetState/HLock/HSetState tuple.
  3217.                     //
  3218.                     // -- Quinn, 25 May 1999
  3219.                     
  3220.                     ReleaseResource(thisResourceH);
  3221.                     err = ResError();
  3222.                 } else if (err == resNotFound) {
  3223.                     err = noErr;
  3224.                 }
  3225.             }
  3226.             if (err != noErr) {
  3227.                 break;
  3228.             }
  3229.         }
  3230.     }
  3231.  
  3232.     err2 = BuilderDone(&state, packedPrefs);
  3233.     if (err == noErr) {
  3234.         err = err2;
  3235.     }
  3236.     MoreAssertQ(err != noErr && packedPrefs == nil || err == noErr && packedPrefs != nil);
  3237.     return err;
  3238. }
  3239.  
  3240. static OSStatus Set1ResourceFromPtr(OSType theType, SInt16 theID, void *dataPtr, ByteCount dataSize)
  3241.     // God I hate the Resource Manager API.  There's no
  3242.     // simple way of saying "set this resource to this value".
  3243.     // Instead, you have to get it, see whether it exists,
  3244.     // if it does, change it and mark it changed, if it doesn't,
  3245.     // create it.  Blurgh.  All the while checking for weirdo
  3246.     // errors and handling the facts that a) you can't release
  3247.     // changed resources (otherwise there's no copy of the handle
  3248.     // containing the data for the Resource Manager to write
  3249.     // when you call UpdateResFile), and b) AddResource converts
  3250.     // a memory handle to a resource handle, but if it fails you
  3251.     // still have to dispose of the memory.
  3252. {
  3253.     OSStatus err;
  3254.     Handle thisResourceH;
  3255.     Handle tmpDataH;
  3256.  
  3257.     thisResourceH = Get1Resource(theType, theID);
  3258.     err = CheckResError(thisResourceH);
  3259.     if (err == noErr) {
  3260.         err = PtrToXHand(dataPtr, thisResourceH, dataSize);
  3261.         if (err == noErr) {
  3262.             ChangedResource(thisResourceH);
  3263.             err = ResError();
  3264.         }
  3265.     } else if (err == resNotFound) {
  3266.         err = PtrToHand(dataPtr, &tmpDataH, dataSize);
  3267.         if (err == noErr) {
  3268.             AddResource(tmpDataH, theType, theID, "\p");
  3269.             err = ResError();
  3270.             if (err != noErr) {
  3271.                 DisposeHandle(tmpDataH);
  3272.                 MoreAssertQ(MemError() == noErr);
  3273.             }
  3274.         }
  3275.     }
  3276.  
  3277.     return err;
  3278. }
  3279.  
  3280. static OSStatus WritePackedPrefsToFile(OSType protocol, SInt16 configID, Handle packedPrefs)
  3281.     // This is a utility routine that simply writes the entire
  3282.     // set of packed preferences to the configuration specified
  3283.     // by configID of the current resource file.
  3284. {
  3285.     OSStatus err;
  3286.     SInt8 s;
  3287.     ByteCount cookie;
  3288.     OSType prefType;
  3289.     ByteCount prefSize;
  3290.     void *prefPtr;
  3291.     Handle cnamHandle;
  3292.     
  3293.     MoreAssertQ(packedPrefs != nil);
  3294.     
  3295.     // Lock down the packed preferences.
  3296.     
  3297.     s = HGetState(packedPrefs);
  3298.     HLock(packedPrefs);
  3299.     MoreAssertQ(MemError() == noErr);
  3300.  
  3301.     // Iterate through each preference, write it out to the database.
  3302.     
  3303.     err = noErr;    
  3304.     cookie = 0;
  3305.     do {
  3306.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefPtr, &prefSize);
  3307.         switch (prefType) {
  3308.             case kOTCfgUserVisibleNamePref:
  3309.                 
  3310.                 // We have to handle the 'pnam' preference as a special
  3311.                 // case.  In legacy files, the user-visible name of the
  3312.                 // configuration is stored as the resource name of the
  3313.                 // 'cnam' resource.  So we need to first write a 'cnam'
  3314.                 // resource then change its name to the name given as the
  3315.                 // value of the 'pnam' preference.
  3316.                 
  3317.                 // ••• Gotcha •••
  3318.                 // The following switch is a bit of a hack.  It seems that
  3319.                 // the OT preference files use a slightly different format
  3320.                 // from the Remote Access files.  Specifically, ARA puts
  3321.                 // the configuration ID of the configuration into the
  3322.                 // resource data of the 'cnam' resource, but OT preference
  3323.                 // files leave the 'cnam' resource empty.  I'm faithfully
  3324.                 // emulating this behaviour, even though I'm pretty sure that
  3325.                 // ARA doesn't care about the contents of the resource
  3326.                 // (I've seen plenty of preference files where the resource
  3327.                 // contents is wrong and ARA still seems to work) primarily
  3328.                 // to prevent the legacy file format drifting any further
  3329.                 // than it also has.  I default to the OT mechanism because
  3330.                 // I'm pretty sure that no one else is going to depend on this.
  3331.                 
  3332.                 switch (protocol) {
  3333.                     case kOTCfgTypeRemote:
  3334.                     case kOTCfgTypeModem:
  3335.                         err = Set1ResourceFromPtr(kOTCfgCompatNamePref, configID, &configID, sizeof(configID));
  3336.                         break;
  3337.                     default:
  3338.                         err = Set1ResourceFromPtr(kOTCfgCompatNamePref, configID, &configID, 0);
  3339.                         break;
  3340.                 }
  3341.                 
  3342.                 // Now update the resource name with the user-visible name
  3343.                 // of the configuration.
  3344.                 
  3345.                 if (err == noErr) {
  3346.                     cnamHandle = Get1Resource(kOTCfgCompatNamePref, configID);
  3347.                     // Assert: We just created this resource, where did it go!
  3348.                     MoreAssertQ( CheckResError(cnamHandle) == noErr );
  3349.                     if (cnamHandle != nil) {
  3350.                         // Assert: prefPtr should point to a Pascal string.
  3351.                         MoreAssertQ( *((UInt8 *) prefPtr) + 1 == prefSize );
  3352.                         SetResInfo(cnamHandle, configID, prefPtr);
  3353.                     }
  3354.                 }
  3355.                 break;
  3356.             case 0:
  3357.                 // do nothing, termination condition
  3358.                 break;
  3359.             default:
  3360.                 err = Set1ResourceFromPtr(prefType, configID, prefPtr, prefSize);
  3361.                 break;
  3362.         }
  3363.     } while (err == noErr && prefType != 0);
  3364.     
  3365.     // Clean up.
  3366.     
  3367.     HSetState(packedPrefs, s);
  3368.     
  3369.     return err;
  3370. }
  3371.  
  3372. static SInt16 GenerateUniqueNewConfigID(void)
  3373.     // Generate a unique ID for the new configuration.  The loop
  3374.     // might seem a bit gung ho, but it's actually fairly fail secure.
  3375.     // As a rule, if it fails Get1Resource will return nil, and we
  3376.     // fall out.
  3377. {
  3378.     SInt16 newConfigID;
  3379.     
  3380.     newConfigID = 128;
  3381.     while ( Get1Resource(kOTCfgCompatNamePref, newConfigID) != nil ) {
  3382.         newConfigID += 1;
  3383.     }
  3384.     return newConfigID;
  3385. }
  3386.  
  3387. static void DeleteHalfCreatedConfig(Handle packedPrefs, SInt16 configID)
  3388.     // If we get halfway through creating a configuration and fail,
  3389.     // we have to make sure there aren't any bits of the new configuration
  3390.     // left lying around in the preferences file.  This routine does this.
  3391.     // It shares a lot of code with DeleteConfigurationFile, but not
  3392.     // really enough to justify one calling the other.  The key differences
  3393.     // are: a) this routine assumes the preference file is already open, and
  3394.     // b) this routine doesn't raise an error if the preference doesn't exist.
  3395. {
  3396.     OSStatus err;
  3397.     UInt32 cookie;
  3398.     OSType prefType;
  3399.     void *junkPtr;
  3400.     ByteCount junkSize;
  3401.     Handle prefToDelete;
  3402.     
  3403.     err = noErr;
  3404.     cookie = 0;
  3405.     do {
  3406.         WriterGetNextPref(&cookie, *packedPrefs, &prefType, &junkPtr, &junkSize);
  3407.         if (prefType != 0) {
  3408.             if (prefType == kOTCfgUserVisibleNamePref) {
  3409.                 prefType = kOTCfgCompatNamePref;
  3410.             }
  3411.             SetResLoad(false);
  3412.             prefToDelete = Get1Resource(prefType, configID);
  3413.             err = CheckResError(prefToDelete);
  3414.             SetResLoad(true);
  3415.             
  3416.             if (err == noErr) {
  3417.                 RemoveResource(prefToDelete);
  3418.                 err = ResError();
  3419.                 if (err == noErr) {
  3420.                     DisposeHandle(prefToDelete);
  3421.                     MoreAssertQ(MemError() == noErr);
  3422.                 }
  3423.             } else if (err == resNotFound) {
  3424.                 err = noErr;
  3425.             }
  3426.         }
  3427.     } while (err == noErr && prefType != 0);
  3428.     MoreAssertQ(err == noErr);
  3429. }
  3430.  
  3431. static OSStatus CreateConfigurationFile(const NSHConfigurationDigest *configurationDigest,
  3432.                                                 NSHConfigurationEntry *createdConfig)
  3433.     // Implementation of NSHCreateConfiguration which uses the legacy
  3434.     // preference files.  See NSHCreateConfiguration's comment in header
  3435.     // file for interface specification.
  3436. {
  3437.     OSStatus err;
  3438.     OSStatus err2;
  3439.     SInt16 oldResFile;
  3440.     SInt16 refNum;
  3441.     SInt16 newConfigID;
  3442.     Handle packedPrefs;
  3443.     
  3444.     MoreAssertQ(configurationDigest != nil);
  3445.  
  3446.     packedPrefs = nil;
  3447.     
  3448.     err = OpenNetworkPrefFile(configurationDigest->fCommon.fProtocol, fsRdWrPerm, &oldResFile, &refNum);
  3449.     if (err == noErr) {
  3450.         newConfigID = GenerateUniqueNewConfigID();
  3451.         
  3452.         // Fill out the entity with the information from the digest.
  3453.         
  3454.         err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, true, &packedPrefs);
  3455.         if (err == noErr) {
  3456.             err = WritePackedPrefsToFile(configurationDigest->fCommon.fProtocol, newConfigID, packedPrefs);
  3457.             if (err != noErr) {
  3458.                 // If we get an error, make sure to eelete any bits of the
  3459.                 // half-created config.
  3460.                 DeleteHalfCreatedConfig(packedPrefs, newConfigID);
  3461.             }
  3462.         }
  3463.         
  3464.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3465.         if (err == noErr) {
  3466.             err = err2;
  3467.         }
  3468.     }
  3469.     
  3470.     // If the client requested the NSHConfigurationEntry for the new
  3471.     // configuration, give it to them.
  3472.     
  3473.     if (err == noErr && createdConfig != nil) {
  3474.         OTMemzero(createdConfig, sizeof(*createdConfig));
  3475.         BlockMoveData(configurationDigest->fCommon.fConfigName, createdConfig->name, sizeof(createdConfig->name));
  3476.         createdConfig->cookie = newConfigID;
  3477.         createdConfig->cookie4 = configurationDigest->fCommon.fProtocol;
  3478.     }
  3479.  
  3480.     // Clean up.
  3481.     
  3482.     if (packedPrefs != nil) {
  3483.         DisposeHandle(packedPrefs);
  3484.         MoreAssertQ(MemError() == noErr);
  3485.     }
  3486.     
  3487.     return err;
  3488. }
  3489.  
  3490. static OSStatus DuplicateConfigurationFile(const NSHConfigurationEntry *config,
  3491.                                            ConstStr255Param newConfigName,
  3492.                                                 NSHConfigurationEntry *createdConfig)
  3493.     // Implementation of NSHDuplicateConfiguration which uses the legacy
  3494.     // preference files.  See NSHDuplicateConfiguration's comment in header
  3495.     // file for interface specification.
  3496. {
  3497.     OSStatus err;
  3498.     OSStatus err2;
  3499.     SInt16 oldResFile;
  3500.     SInt16 refNum;
  3501.     Handle packedPrefs;
  3502.     SInt16 newConfigID;
  3503.     Handle cnamHandle;
  3504.  
  3505.     MoreAssertQ(config != nil);
  3506.     MoreAssertQ(newConfigName != nil);
  3507.  
  3508.     packedPrefs = nil;
  3509.     
  3510.     err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  3511.     if (err == noErr) {
  3512.         newConfigID = GenerateUniqueNewConfigID();
  3513.         
  3514.         // Read the preferences from the original configuration
  3515.         // and write them to the new configuration.
  3516.         
  3517.         if (err == noErr) {
  3518.             err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs);
  3519.         }
  3520.         if (err == noErr) {
  3521.             err = WritePackedPrefsToFile(config->cookie4, newConfigID, packedPrefs);
  3522.         }
  3523.         
  3524.         // Set the name of the new configuration.
  3525.         
  3526.         if (err == noErr) {
  3527.             cnamHandle = Get1Resource(kOTCfgCompatNamePref, newConfigID);
  3528.             err = CheckResError(cnamHandle);
  3529.             if (err == noErr) {
  3530.                 SetResInfo(cnamHandle, newConfigID, newConfigName);
  3531.             }
  3532.         }
  3533.         
  3534.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3535.         if (err == noErr) {
  3536.             err = err2;
  3537.         }
  3538.     }
  3539.     
  3540.     // If the client requested the NSHConfigurationEntry for the new
  3541.     // configuration, give it to them.
  3542.     
  3543.     if (err == noErr && createdConfig != nil) {
  3544.         *createdConfig = *config;
  3545.         BlockMoveData(newConfigName, createdConfig->name, sizeof(createdConfig->name));
  3546.         createdConfig->cookie = newConfigID;
  3547.     }
  3548.     
  3549.     // Clean up.
  3550.     
  3551.     if (packedPrefs != nil) {
  3552.         DisposeHandle(packedPrefs);
  3553.         MoreAssertQ(MemError() == noErr);
  3554.     }
  3555.  
  3556.     return err;
  3557. }
  3558.  
  3559. static OSStatus GetConfigurationFile(const NSHConfigurationEntry *config,
  3560.                                                 NSHConfigurationDigest *configurationDigest)
  3561.     // Implementation of NSHGetConfiguration which uses the legacy
  3562.     // preference files.  See NSHGetConfiguration's comment in header
  3563.     // file for interface specification.
  3564. {
  3565.     OSStatus err;
  3566.     OSStatus err2;
  3567.     SInt16 oldResFile;
  3568.     SInt16 refNum;
  3569.     Handle packedPrefs;
  3570.     
  3571.     MoreAssertQ(config != nil);
  3572.     MoreAssertQ(configurationDigest != nil);
  3573.     
  3574.     packedPrefs = nil;
  3575.     
  3576.     err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  3577.     if (err == noErr) {
  3578.         err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs);
  3579.         if (err == noErr) {
  3580.             err = BuildConfigurationDigestFromPackedPrefs(config->cookie4, packedPrefs, configurationDigest);
  3581.         }
  3582.         
  3583.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3584.         if (err == noErr) {
  3585.             err = err2;
  3586.         }
  3587.     }
  3588.  
  3589.     if (packedPrefs != nil) {
  3590.         DisposeHandle(packedPrefs);
  3591.         MoreAssertQ(MemError() == noErr);
  3592.     }
  3593.     
  3594.     return err;
  3595. }
  3596.  
  3597. static OSStatus SetConfigurationFile(const NSHConfigurationEntry *config,
  3598.                                                 const NSHConfigurationDigest *configurationDigest)
  3599.     // Implementation of NSHSetConfiguration which uses the legacy
  3600.     // preference files.  See NSHSetConfiguration's comment in header
  3601.     // file for interface specification.
  3602. {
  3603.     OSStatus err;
  3604.     OSStatus err2;
  3605.     SInt16 oldResFile;
  3606.     SInt16 refNum;
  3607.     Handle packedPrefs;
  3608.     SInt16 currentConfigID;
  3609.  
  3610.     MoreAssertQ(config != nil);
  3611.     MoreAssertQ(configurationDigest != nil);
  3612.  
  3613.     packedPrefs = nil;
  3614.     
  3615.     err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  3616.     if (err == noErr) {
  3617.         err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, false, &packedPrefs);
  3618.         if (err == noErr) {
  3619.             err = WritePackedPrefsToFile(config->cookie4, config->cookie, packedPrefs);
  3620.         }
  3621.         
  3622.         // If we're modifying the current configuration, tell the protocol
  3623.         // stacks about it.
  3624.         
  3625.         if (err == noErr) {
  3626.             err = GetCurrentConfigFromPrefFile(¤tConfigID);
  3627.             if (err == noErr) {
  3628.                 if (config->cookie == currentConfigID) {
  3629.                     err = CommitChangesToPrefFile(config->cookie4, refNum, config->cookie);
  3630.                 }
  3631.             }
  3632.         }
  3633.  
  3634.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3635.         if (err == noErr) {
  3636.             err = err2;
  3637.         }
  3638.     }
  3639.     
  3640.     // Clean up.
  3641.     
  3642.     if (packedPrefs != nil) {
  3643.         DisposeHandle(packedPrefs);
  3644.         MoreAssertQ(MemError() == noErr);
  3645.     }
  3646.  
  3647.     return err;
  3648. }
  3649.  
  3650. static OSStatus DeleteConfigurationFile(const NSHConfigurationEntry *config)
  3651.     // Implementation of NSHDeleteConfiguration which uses the legacy
  3652.     // preference files.  See NSHDeleteConfiguration's comment in header
  3653.     // file for interface specification.
  3654. {
  3655.     OSStatus err;
  3656.     OSStatus err2;
  3657.     SInt16 oldResFile;
  3658.     SInt16 refNum;
  3659.     SInt16 currentConfigID;
  3660.     Handle packedPrefs;
  3661.     UInt32 cookie;
  3662.     OSType prefType;
  3663.     void *junkPtr;
  3664.     ByteCount junkSize;
  3665.     Handle prefToDelete;
  3666.         
  3667.     MoreAssertQ(config != nil);
  3668.  
  3669.     packedPrefs = nil;
  3670.     
  3671.     err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  3672.     if (err == noErr) {
  3673.         
  3674.         // You're not allowed to delete an active configuration.
  3675.         
  3676.         err = GetCurrentConfigFromPrefFile(¤tConfigID);
  3677.         if (err == noErr) {
  3678.             if (config->cookie == currentConfigID) {
  3679.                 err = -11;
  3680.             }
  3681.         }
  3682.  
  3683.         // Now read a slice out of the preference file, ie all
  3684.         // the resource with ID of config->cookie regardless of their
  3685.         // type.  We do this in before starting to delete stuff because
  3686.         // a) we already have the code (we share it with various other
  3687.         // routines) and b) the API to the Resource Manager is based
  3688.         // on indexes, so if you start deleting stuff midway through
  3689.         // things get very confusing.
  3690.         //
  3691.         // This wastes some memory (we read in all the preference data
  3692.         // even though we're never going to use it), but that's the price
  3693.         // you pay for having me write the code for you (-:
  3694.  
  3695.         if (err == noErr) {
  3696.             err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs);
  3697.         }
  3698.         
  3699.         // Now delete each preference in the slice from the resource file.
  3700.         // We exercise two little known Resource Manager features here.
  3701.         // Firstly, we SetResLoad to false before calling Get1Resource.
  3702.         // This saves memory (and time) by preventing the Resource Manager
  3703.         // from actually loading the preference data into memory.
  3704.         // Secondly, we must DisposeHandle(prefToDelete) after we've
  3705.         // successfully removed it from the resource file.  Removing
  3706.         // a handle from a resource file leaves you with a memory
  3707.         // handle which you must dispose lest you leak.
  3708.         
  3709.         if (err == noErr) {
  3710.             HLock(packedPrefs);        // Unilaterally lock because we're going to dispose at the end of this routine anyway.
  3711.             cookie = 0;
  3712.             do {
  3713.                 WriterGetNextPref(&cookie, *packedPrefs, &prefType, &junkPtr, &junkSize);
  3714.                 if (prefType != 0) {
  3715.                     if (prefType == kOTCfgUserVisibleNamePref) {
  3716.                         prefType = kOTCfgCompatNamePref;
  3717.                     }
  3718.                     SetResLoad(false);
  3719.                     prefToDelete = Get1Resource(prefType, config->cookie);
  3720.                     err = CheckResError(prefToDelete);
  3721.                     SetResLoad(true);
  3722.                     
  3723.                     if (err == noErr) {
  3724.                         RemoveResource(prefToDelete);
  3725.                         err = ResError();
  3726.                     }
  3727.                     if (err == noErr) {
  3728.                         DisposeHandle(prefToDelete);
  3729.                         MoreAssertQ(MemError() == noErr);
  3730.                     }
  3731.                 }
  3732.             } while (err == noErr && prefType != 0);
  3733.         }
  3734.  
  3735.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3736.         if (err == noErr) {
  3737.             err = err2;
  3738.         }
  3739.     }
  3740.     
  3741.     // Clean up.
  3742.     
  3743.     if (packedPrefs != nil) {
  3744.         DisposeHandle(packedPrefs);
  3745.         MoreAssertQ(MemError() == noErr);
  3746.     }
  3747.     
  3748.     return err;
  3749. }
  3750.  
  3751. static OSStatus RenameConfigurationFile(const NSHConfigurationEntry *config,
  3752.                                               ConstStr255Param newConfigName,
  3753.                                               NSHConfigurationEntry *newConfig)
  3754.     // Implementation of NSHRenameConfiguration which uses the legacy
  3755.     // preference files.  See NSHRenameConfiguration's comment in header
  3756.     // file for interface specification.
  3757. {
  3758.     OSStatus err;
  3759.     OSStatus err2;
  3760.     SInt16 oldResFile;
  3761.     SInt16 refNum;
  3762.     Handle cnamHandle;
  3763.     
  3764.     MoreAssertQ(config != nil);
  3765.     MoreAssertQ(newConfigName != nil);
  3766.     
  3767.     err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum);
  3768.     if (err == noErr) {
  3769.         cnamHandle = Get1Resource(kOTCfgCompatNamePref, config->cookie);
  3770.         err = CheckResError(cnamHandle);
  3771.         if (err == noErr) {
  3772.             SetResInfo(cnamHandle, config->cookie, newConfigName);
  3773.         }
  3774.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  3775.         if (err == noErr) {
  3776.             err = err2;
  3777.         }
  3778.     }
  3779.  
  3780.     // If the client requested the NSHConfigurationEntry for the renamed
  3781.     // configuration, give it to them.
  3782.  
  3783.     if (err == noErr && newConfig != nil) {
  3784.         *newConfig = *config;
  3785.         BlockMoveData(newConfigName, newConfig->name, sizeof(newConfig->name));
  3786.     }
  3787.         
  3788.     return err;
  3789. }
  3790.  
  3791. /////////////////////////////////////////////////////////////////
  3792. #pragma mark ----- Internet Setup Abstraction ------
  3793.  
  3794. extern pascal OSStatus NSHCreateConfiguration(const NSHConfigurationDigest *configurationDigest,
  3795.                                                 NSHConfigurationEntry *createdConfig)
  3796.     // See comments in interface part.
  3797. {
  3798.     OSStatus err;
  3799.     
  3800.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3801.         #if TARGET_RT_MAC_CFM
  3802.             err = CreateConfigurationDatabase(configurationDigest, createdConfig);
  3803.         #else
  3804.             // Network Setup has no Mixed Mode glue.  When running
  3805.             // code on a PowerPC with Network Setup available, you
  3806.             // should either compile your code as Fat or, if that's
  3807.             // infeasible, write your own Mixed Mode glue.
  3808.             return -5;
  3809.         #endif
  3810.     } else {
  3811.         err = CreateConfigurationFile(configurationDigest, createdConfig);
  3812.     }
  3813.     return err;
  3814. }
  3815.  
  3816. extern pascal OSStatus NSHDuplicateConfiguration(NSHConfigurationEntry *config,
  3817.                                                  ConstStr255Param newConfigName,
  3818.                                                  NSHConfigurationEntry *createdConfig)
  3819.     // See comments in interface part.
  3820. {
  3821.     OSStatus err;
  3822.     
  3823.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3824.         #if TARGET_RT_MAC_CFM
  3825.             err = DuplicateConfigurationDatabase(config, newConfigName, createdConfig);
  3826.         #else
  3827.             // Network Setup has no Mixed Mode glue.  When running
  3828.             // code on a PowerPC with Network Setup available, you
  3829.             // should either compile your code as Fat or, if that's
  3830.             // infeasible, write your own Mixed Mode glue.
  3831.             return -5;
  3832.         #endif
  3833.     } else {
  3834.         err = DuplicateConfigurationFile(config, newConfigName, createdConfig);
  3835.     }
  3836.     return err;
  3837. }
  3838.  
  3839. extern pascal OSStatus NSHGetConfiguration(const NSHConfigurationEntry *config,
  3840.                                                 NSHConfigurationDigest *configurationDigest)
  3841.     // See comments in interface part.
  3842. {
  3843.     OSStatus err;
  3844.     
  3845.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3846.         #if TARGET_RT_MAC_CFM
  3847.             err = GetConfigurationDatabase(config, configurationDigest);
  3848.         #else
  3849.             // Network Setup has no Mixed Mode glue.  When running
  3850.             // code on a PowerPC with Network Setup available, you
  3851.             // should either compile your code as Fat or, if that's
  3852.             // infeasible, write your own Mixed Mode glue.
  3853.             return -5;
  3854.         #endif
  3855.     } else {
  3856.         err = GetConfigurationFile(config, configurationDigest);
  3857.     }
  3858.     return err;
  3859. }
  3860.  
  3861. extern pascal OSStatus NSHSetConfiguration(const NSHConfigurationEntry *config,
  3862.                                                 const NSHConfigurationDigest *configurationDigest)
  3863.     // See comments in interface part.
  3864. {
  3865.     OSStatus err;
  3866.     
  3867.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3868.         #if TARGET_RT_MAC_CFM
  3869.             err = SetConfigurationDatabase(config, configurationDigest);
  3870.         #else
  3871.             // Network Setup has no Mixed Mode glue.  When running
  3872.             // code on a PowerPC with Network Setup available, you
  3873.             // should either compile your code as Fat or, if that's
  3874.             // infeasible, write your own Mixed Mode glue.
  3875.             return -5;
  3876.         #endif
  3877.     } else {
  3878.         err = SetConfigurationFile(config, configurationDigest);
  3879.     }
  3880.     return err;
  3881. }
  3882.  
  3883. extern pascal OSStatus NSHDeleteConfiguration(const NSHConfigurationEntry *config)
  3884.     // See comments in interface part.
  3885. {
  3886.     OSStatus err;
  3887.     
  3888.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3889.         #if TARGET_RT_MAC_CFM
  3890.             err = DeleteConfigurationDatabase(config);
  3891.         #else
  3892.             // Network Setup has no Mixed Mode glue.  When running
  3893.             // code on a PowerPC with Network Setup available, you
  3894.             // should either compile your code as Fat or, if that's
  3895.             // infeasible, write your own Mixed Mode glue.
  3896.             return -5;
  3897.         #endif
  3898.     } else {
  3899.         err = DeleteConfigurationFile(config);
  3900.     }
  3901.     return err;
  3902. }
  3903.  
  3904. extern pascal OSStatus NSHRenameConfiguration(const NSHConfigurationEntry *config,
  3905.                                               ConstStr255Param newConfigName,
  3906.                                               NSHConfigurationEntry *newConfig)
  3907.     // See comments in interface part.
  3908. {
  3909.     OSStatus err;
  3910.     
  3911.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  3912.         #if TARGET_RT_MAC_CFM
  3913.             err = RenameConfigurationDatabase(config, newConfigName, newConfig);
  3914.         #else
  3915.             // Network Setup has no Mixed Mode glue.  When running
  3916.             // code on a PowerPC with Network Setup available, you
  3917.             // should either compile your code as Fat or, if that's
  3918.             // infeasible, write your own Mixed Mode glue.
  3919.             return -5;
  3920.         #endif
  3921.     } else {
  3922.         err = RenameConfigurationFile(config, newConfigName, newConfig);
  3923.     }
  3924.     return err;
  3925. }
  3926.  
  3927. /////////////////////////////////////////////////////////////////
  3928. #pragma mark ----- Database Will Dial ------
  3929.  
  3930. static OSStatus GetPortNameFromTCPPrefs(Ptr buffer, SInt32 prefSize, char *portName)
  3931.     // This routine takes the address and size of an 'iitf' preference
  3932.     // and extracts the port name from the first interface.
  3933. {
  3934.     OSStatus err;
  3935.     UInt16 interfaceCount;
  3936.     Ptr cursor;
  3937.     NSHTCPv4ConfigurationDigest firstInterface;
  3938.     UInt8 portNameLength;
  3939.     
  3940.     // Get the count of interfaces, checking for possibly bogus
  3941.     // preference data.
  3942.     
  3943.     err = noErr;
  3944.     if (prefSize < sizeof(UInt16)) {
  3945.         err = -1;
  3946.     }
  3947.     if (err == noErr) {
  3948.         interfaceCount = *((UInt16 *)buffer);
  3949.         if (interfaceCount < 1) {
  3950.             err = -1;
  3951.         }
  3952.     }
  3953.     
  3954.     // Unpack the first interface out of the 'iitf'.
  3955.     
  3956.     if (err == noErr) {
  3957.         cursor = buffer + sizeof(UInt16);
  3958.         UnpackTCPPrefs(&cursor, &firstInterface);
  3959.  
  3960.         // Assert: Did not consume correct number of bytes
  3961.         MoreAssertQ( interfaceCount > 1 || (cursor == buffer + prefSize) );
  3962.     }
  3963.     
  3964.     // Copy the port name out of the unpacked interface.
  3965.     
  3966.     if (err == noErr) {
  3967.         portNameLength = firstInterface.fHintPortName[0];
  3968.         if ( portNameLength > kMaxProviderNameLength) {
  3969.             err = -1;
  3970.         } else {
  3971.  
  3972.             // Poor Man's C2PString avoids me having to figure
  3973.             // out which wacky library CodeWarrior wants me to link with
  3974.             // today!
  3975.             
  3976.             BlockMoveData(firstInterface.fHintPortName + 1, portName, portNameLength);
  3977.             portName[ portNameLength ] = 0;
  3978.         }
  3979.     }
  3980.  
  3981.     return err;
  3982. }
  3983.  
  3984. static OSStatus GetInfoForTCPEntity(const MNSDatabaseRef *ref, const CfgEntityRef *entityID,
  3985.                                     Boolean *enabled, char *portName)
  3986.     // This routine returns the enabled status and port name
  3987.     // for the TCP/IP preferences entity described by entityID
  3988.     // in the ref database.
  3989. {    
  3990.     OSStatus err;
  3991.     SInt16 enabledInt;
  3992.     Ptr buffer;
  3993.     ByteCount prefSize;
  3994.  
  3995.     buffer = nil;
  3996.  
  3997.     // First return enabled using the simple API.
  3998.     
  3999.     err = MNSGetFixedSizePref(ref, entityID, kOTCfgTCPUnloadAttrPref, &enabledInt, sizeof(SInt16));
  4000.     if (err == noErr) {
  4001.         *enabled = (enabledInt != 3);
  4002.     }
  4003.     
  4004.     // Now return the port name.  Now call the variable sized
  4005.     // API to get the 'iitf' resource and then extract the port name 
  4006.     // from the preference buffer.
  4007.     
  4008.     if (err == noErr) {
  4009.         err = MNSGetPref(ref, entityID, kOTCfgTCPInterfacesPref, &buffer, &prefSize);
  4010.     }
  4011.     if (err == noErr) {
  4012.         err = GetPortNameFromTCPPrefs(buffer, prefSize, portName);
  4013.     }
  4014.     
  4015.     // Clean up.
  4016.     
  4017.     if (buffer != nil) {
  4018.         DisposePtr(buffer);
  4019.         MoreAssertQ(MemError() == noErr);
  4020.     }
  4021.     return err;
  4022. }
  4023.  
  4024. static OSStatus GetTCPInfoFromDatabase(Boolean *enabled, char *portName)
  4025.     // The high-level entry point into the configuration database
  4026.     // implementation.  We open the database, find the current
  4027.     // TCP entity and read the info we need out of that entity.
  4028. {
  4029.     OSStatus err;
  4030.     OSStatus err2;
  4031.     MNSDatabaseRef ref;
  4032.     CfgEntityRef currentTCPEntity;
  4033.  
  4034.     err = MNSOpenDatabase(&ref, false);
  4035.     if (err == noErr) {
  4036.         err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity);
  4037.         if (err == noErr) {
  4038.             err = GetInfoForTCPEntity(&ref, ¤tTCPEntity, enabled, portName);
  4039.         }
  4040.  
  4041.         err2 = MNSCloseDatabase(&ref, false);
  4042.         if (err == noErr) {
  4043.             err = err2;
  4044.         }
  4045.     }
  4046.     return err;
  4047. }
  4048.  
  4049. /////////////////////////////////////////////////////////////////
  4050. #pragma mark ----- File Will Dial ------
  4051.  
  4052. static OSStatus GetTCPInfoFromFile(Boolean *enabled, char *portName)
  4053.     // This is the high-level entry point into the direct file
  4054.     // access implementation.  It simply finds the preferences
  4055.     // file and reads the preferences out directly.
  4056. {
  4057.     OSStatus err;
  4058.     OSStatus err2;
  4059.     SInt16 oldResFile;
  4060.     SInt16 refNum;
  4061.     Handle unldResource;
  4062.     Handle iitfResource;
  4063.     SInt8  s;
  4064.     SInt16 config;
  4065.     
  4066.     err = OpenNetworkPrefFile(kOTCfgTypeTCPv4, fsRdPerm, &oldResFile, &refNum);
  4067.     if (err == noErr) {
  4068.         err = GetCurrentConfigFromPrefFile(&config);
  4069.  
  4070.         if (err == noErr) {
  4071.             unldResource = Get1Resource(kOTCfgTCPUnloadAttrPref, config);
  4072.             err = CheckResError(unldResource);
  4073.         }
  4074.         if (err == noErr) {
  4075.             *enabled = ( **((SInt16 **) unldResource) != 3);
  4076.         }
  4077.  
  4078.         if (err == noErr) {
  4079.             iitfResource = Get1Resource(kOTCfgTCPInterfacesPref, config);
  4080.             err = CheckResError(iitfResource);
  4081.         }
  4082.  
  4083.         if (err == noErr) {
  4084.             s = HGetState(iitfResource);
  4085.             HLock(iitfResource);
  4086.             err = GetPortNameFromTCPPrefs(*iitfResource, GetHandleSize(iitfResource), portName);
  4087.             HSetState(iitfResource, s);
  4088.         }
  4089.         
  4090.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  4091.         if (err == noErr) {
  4092.             err = err2;
  4093.         }
  4094.     }
  4095.     
  4096.     return err;
  4097. }
  4098.  
  4099. /////////////////////////////////////////////////////////////////
  4100. #pragma mark ----- Will Dial Abstraction -----
  4101.  
  4102. static OSStatus GetTCPInfo(Boolean *enabled, char *portName)
  4103.     // A dispatcher.  If the config database is available,
  4104.     // we call it, otherwise we fall back to reading the
  4105.     // preferences file directly.
  4106. {
  4107.     OSStatus err;
  4108.     
  4109.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  4110.         #if TARGET_RT_MAC_CFM
  4111.             err = GetTCPInfoFromDatabase(enabled, portName);
  4112.         #else
  4113.             // Network Setup has no Mixed Mode glue.  When running
  4114.             // code on a PowerPC with Network Setup available, you
  4115.             // should either compile your code as Fat or, if that's
  4116.             // infeasible, write your own Mixed Mode glue.
  4117.             return -5;
  4118.         #endif
  4119.     } else {
  4120.         err = GetTCPInfoFromFile(enabled, portName);
  4121.     }
  4122.     return err;
  4123. }
  4124.  
  4125. extern pascal OSStatus NSHTCPWillDial(UInt32 *willDial)
  4126.     // The main entry point.  We call our core
  4127.     // implementation and then generate the result
  4128.     // based on the returned information.
  4129. {
  4130.     OSStatus err;
  4131.     InetInterfaceInfo info;
  4132.     Boolean enabled;
  4133.     char currentPortName[kMaxProviderNameSize];
  4134.     OTPortRecord portRecord;
  4135.     
  4136.     MoreAssertQ(willDial != nil);
  4137.     
  4138.     *willDial = kNSHTCPDialUnknown;
  4139.     
  4140.     err = noErr;
  4141.     if ( kUseInetInterfaceInfo && OTInetGetInterfaceInfo(&info, kDefaultInetInterface) == noErr) {
  4142.     
  4143.         // The TCP/IP stack is already loaded.  With the current
  4144.         // way TCP/IP is organised, the stack being loaded implies
  4145.         // that we're already dialled in.
  4146.         
  4147.         *willDial = kNSHTCPDialNo;
  4148.         
  4149.     } else {
  4150.         err = GetTCPInfo(&enabled, currentPortName);
  4151.         if (err == noErr) {
  4152.             if (enabled) {
  4153.                 if ( OTStrEqual(currentPortName, "ddp") ) { 
  4154.  
  4155.                     // A special case for MacIP, because "ddp" does
  4156.                     // not have an active port if AppleTalk is disabled.
  4157.                     
  4158.                     *willDial = kNSHTCPDialNo;
  4159.                     
  4160.                 } else if ( OTFindPort(&portRecord, currentPortName) ) {
  4161.                 
  4162.                     // We know the port.  Look at the device type
  4163.                     // to decide whether we might dial.
  4164.                 
  4165.                     switch ( OTGetDeviceTypeFromPortRef(portRecord.fRef) ) {
  4166.                         case kOTADEVDevice:
  4167.                         case kOTIRTalkDevice:
  4168.                         case kOTSMDSDevice:
  4169.                             // Assert: TCP shouldn't be using this link type
  4170.                             MoreAssertQ(false);
  4171.                             *willDial = kNSHTCPDialNo;
  4172.                             break;
  4173.                             
  4174.                         case kOTISDNDevice:
  4175.                         case kOTATMDevice:
  4176.                         case kOTSerialDevice:
  4177.                         case kOTModemDevice:
  4178.                             // Assert: TCP shouldn't be using this link type
  4179.                             MoreAssertQ(false);
  4180.                             *willDial = kNSHTCPDialYes;
  4181.                             break;
  4182.  
  4183.                         case kOTEthernetDevice:
  4184.                             // For Ethernet, special case AOL because, well, they're special.
  4185.                             if ( OTStrEqual(currentPortName, "AOLLink0") ) {
  4186.                                 *willDial = kNSHTCPDialYes;
  4187.                             } else {
  4188.                                 *willDial = kNSHTCPDialNo;
  4189.                             }
  4190.                             break;
  4191.                             
  4192.                         case kOTLocalTalkDevice:
  4193.                         case kOTTokenRingDevice:
  4194.                         case kOTFastEthernetDevice:
  4195.                         case kOTFDDIDevice:
  4196.                         case kOTIrDADevice:
  4197.                         case kOTATMSNAPDevice:
  4198.                         case kOTFibreChannelDevice:
  4199.                         case kOTFireWireDevice:
  4200.                             *willDial = kNSHTCPDialNo;
  4201.                             break;
  4202.  
  4203.                         case kOTMDEVDevice:
  4204.                         case kOTSLIPDevice:
  4205.                         case kOTPPPDevice:
  4206.                             *willDial = kNSHTCPDialYes;
  4207.                             break;
  4208.  
  4209.                         default:
  4210.                             MoreAssertQ(*willDial == kNSHTCPDialUnknown);
  4211.                             break;
  4212.                     }
  4213.                 } else {
  4214.                     err = -1;
  4215.                 }
  4216.             } else {
  4217.                 *willDial = kNSHTCPDialTCPDisabled;
  4218.             }
  4219.         }
  4220.     }
  4221.     
  4222.     return err;
  4223. }
  4224.  
  4225. /////////////////////////////////////////////////////////////////
  4226. #pragma mark ----- DHCP Release -----
  4227.  
  4228. static pascal OSStatus DHCPReleaseDatabase(void)
  4229.     // Implementation of NSHDHCPRelease which uses the Network Setup
  4230.     // database.  See NSHDHCPRelease's comment in header
  4231.     // file for interface specification.
  4232. {
  4233.     OSStatus err;
  4234.     OSStatus err2;
  4235.     MNSDatabaseRef ref;
  4236.     CfgEntityRef currentTCPEntity;
  4237.     OTCfgTCPUnloadAttr oldUnloadAttr;
  4238.     OTCfgTCPUnloadAttr newUnloadAttr;
  4239.     const UInt8 dummyPref[16] = {0};
  4240.     
  4241.     // Start by forcing the TCP/IP stack to unload (by deactivating it), saving
  4242.     // the old state in oldUnloadAttr.
  4243.     
  4244.     err = MNSOpenDatabase(&ref, true);
  4245.     if (err == noErr) {
  4246.         err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity);
  4247.         if (err == noErr) {
  4248.             err = MNSGetFixedSizePref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &oldUnloadAttr, sizeof(oldUnloadAttr));
  4249.         }
  4250.         if (err == noErr) {
  4251.             newUnloadAttr = kOTCfgTCPInactive;
  4252.             err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &newUnloadAttr, sizeof(newUnloadAttr));
  4253.         }
  4254.         err2 = MNSCloseDatabase(&ref, err == noErr);
  4255.         if (err == noErr) {
  4256.             err = err2;
  4257.         }
  4258.     }
  4259.     
  4260.     // Then reactive the TCP/IP stack, zeroing the 'dclt' preference in the process.
  4261.     
  4262.     if (err == noErr) {
  4263.         err = MNSOpenDatabase(&ref, true);
  4264.         if (err == noErr) {
  4265.             err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity);
  4266.             if (err == noErr) {
  4267.                 newUnloadAttr = kOTCfgTCPInactive;
  4268.                 err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &oldUnloadAttr, sizeof(oldUnloadAttr));
  4269.             }
  4270.             if (err == noErr) {
  4271.                 err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPDHCPLeaseInfoPref, dummyPref, sizeof(dummyPref));
  4272.             }
  4273.             err2 = MNSCloseDatabase(&ref, err == noErr);
  4274.             if (err == noErr) {
  4275.                 err = err2;
  4276.             }
  4277.         }
  4278.     }
  4279.     
  4280.     return err;
  4281. }
  4282.  
  4283. static pascal OSStatus DHCPReleaseFile(void)
  4284.     // Implementation of NSHDHCPRelease which uses the legacy
  4285.     // preference files.  See NSHDHCPRelease's comment in header
  4286.     // file for interface specification.
  4287. {
  4288.     OSStatus err;
  4289.     OSStatus err2;
  4290.     SInt16 refNum;
  4291.     SInt16 oldResFile;
  4292.     OTCfgTCPUnloadAttr **unldResourceH;
  4293.     SInt16 config;
  4294.     OTCfgTCPUnloadAttr oldUnloadAttr;
  4295.     Handle dcltResourceH;
  4296.  
  4297.     err = OpenNetworkPrefFile(kOTCfgTypeTCPv4, fsRdWrPerm, &oldResFile, &refNum);
  4298.     if (err == noErr) {
  4299.         err = GetCurrentConfigFromPrefFile(&config);
  4300.  
  4301.         // Start by forcing the TCP/IP stack to unload (by deactivating it), saving
  4302.         // the old state in oldUnloadAttr.
  4303.  
  4304.         if (err == noErr) {
  4305.             unldResourceH = (OTCfgTCPUnloadAttr **) Get1Resource(kOTCfgTCPUnloadAttrPref, config);
  4306.             err = CheckResError(unldResourceH);
  4307.         }
  4308.         if (err == noErr && GetHandleSize( (Handle) unldResourceH) != sizeof(OTCfgTCPUnloadAttr)) {
  4309.             err = -1;
  4310.         }
  4311.         if (err == noErr) {
  4312.             oldUnloadAttr = **unldResourceH;
  4313.             **unldResourceH = kOTCfgTCPInactive;
  4314.             ChangedResource( (Handle) unldResourceH);
  4315.             err = ResError();
  4316.             if (err == noErr) {
  4317.                 UpdateResFile(refNum);
  4318.                 err = ResError();
  4319.             }
  4320.             if (err == noErr) {
  4321.                 err = CommitChangesToPrefFile(kOTCfgTypeTCPv4, refNum, config);
  4322.             }
  4323.         }
  4324.         
  4325.         // ••• Gotcha •••
  4326.         // In some circumstances, CommitChangesToPrefFile can close the "TCP/IP Preferences"
  4327.         // file.  The following is a hackaround to detect and safely fail in this case.
  4328.         // I haven't implemented the full workaround, which would be to walk the resource
  4329.         // chain looking to see whether my file has been closed because this problem should
  4330.         // never happen in practice.
  4331.         
  4332.         // My justification?  Well, the code which accidentally closes
  4333.         // the "TCP/IP Preferences" file was added in Mac OS 8.5 as part of the "maintain
  4334.         // the DHCP lease over reboots" effort.  But Mac OS 8.5 and up include the Network
  4335.         // Setup API, so you shouldn't be running this routine on those systems anyway.
  4336.         //
  4337.         // I only discovered this problem because I set kUseNetworkSetup to false to test
  4338.         // the "backward compatibility" code on an Mac OS 8.6 system.
  4339.         //
  4340.         // It turns out that Network Setup is bitten by this problem as well, but it's
  4341.         // less paranoid error checking fails to detect this case.  I've filed a bug
  4342.         // to get OT fixed.  [Radar ID 2354522]
  4343.         
  4344.         if (err == noErr) {
  4345.             if ( CurResFile() != refNum ) {
  4346.                 MoreAssertQ(false);
  4347.                 return -2;
  4348.             }
  4349.         }
  4350.  
  4351.         // Then reactive the TCP/IP stack, zeroing the 'dclt' preference in the process.
  4352.         
  4353.         // Re-get the 'unld' resource because the TCP/IP stack calls ReleaseResource
  4354.         // on it when it reconfigures.
  4355.  
  4356.         if (err == noErr) {
  4357.             unldResourceH = (OTCfgTCPUnloadAttr **) Get1Resource(kOTCfgTCPUnloadAttrPref, config);
  4358.             err = CheckResError(unldResourceH);
  4359.         }
  4360.         if (err == noErr && GetHandleSize( (Handle) unldResourceH) != sizeof(OTCfgTCPUnloadAttr)) {
  4361.             err = -1;
  4362.         }
  4363.         if (err == noErr) {
  4364.             **unldResourceH = oldUnloadAttr;
  4365.             ChangedResource( (Handle) unldResourceH);
  4366.             err = ResError();
  4367.         }
  4368.         if (err == noErr) {
  4369.             dcltResourceH = Get1Resource(kOTCfgTCPDHCPLeaseInfoPref, config);
  4370.             err = CheckResError(dcltResourceH);
  4371.         }
  4372.         if (err == noErr) {
  4373.  
  4374.             // In current OT builds, the 'dclt' preference is 16 bytes.
  4375.             // If this changes, we want to know about it.
  4376.             MoreAssertQ(GetHandleSize(dcltResourceH) == 16);
  4377.             
  4378.             MoreBlockZero(*dcltResourceH, GetHandleSize(dcltResourceH));
  4379.             ChangedResource(dcltResourceH);
  4380.             err = ResError();
  4381.         } else if (err == resNotFound) {
  4382.             err = noErr;
  4383.         }
  4384.         if (err == noErr) {
  4385.             UpdateResFile(refNum);
  4386.             err = ResError();
  4387.         }
  4388.         if (err == noErr) {
  4389.             err = CommitChangesToPrefFile(kOTCfgTypeTCPv4, refNum, config);
  4390.         }
  4391.         
  4392.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  4393.         if (err == noErr) {
  4394.             err = err2;
  4395.         }
  4396.     }
  4397.  
  4398.     return err;
  4399. }
  4400.  
  4401. extern pascal OSStatus NSHDHCPRelease(void)
  4402.     // See comments in interface part.
  4403. {
  4404.     OSStatus err;
  4405.     
  4406.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  4407.         #if TARGET_RT_MAC_CFM
  4408.             err = DHCPReleaseDatabase();
  4409.         #else
  4410.             // Network Setup has no Mixed Mode glue.  When running
  4411.             // code on a PowerPC with Network Setup available, you
  4412.             // should either compile your code as Fat or, if that's
  4413.             // infeasible, write your own Mixed Mode glue.
  4414.             return -5;
  4415.         #endif
  4416.     } else {
  4417.         err = DHCPReleaseFile();
  4418.     }
  4419.     return err;
  4420. }
  4421.  
  4422. /////////////////////////////////////////////////////////////////
  4423. #pragma mark ----- AppleTalk On/Off using Database -----
  4424.  
  4425. static OSStatus IsAppleTalkActiveDatabase(Boolean *active)
  4426.     // Implementation of NSHIsAppleTalkActive which uses the Network Setup
  4427.     // database.  See NSHIsAppleTalkActive's comment in header
  4428.     // file for interface specification.
  4429. {
  4430.     OSStatus err;
  4431.     OSStatus err2;
  4432.     MNSDatabaseRef ref;
  4433.     CfgEntityRef currentAppleTalkEntity;
  4434.     OTCfgATalkGeneral atpfPref;
  4435.  
  4436.     err = MNSOpenDatabase(&ref, false);
  4437.     if (err == noErr) {
  4438.         err = FindCurrentConnection(&ref, kOTCfgTypeAppleTalk, ¤tAppleTalkEntity);
  4439.         if (err == noErr) {
  4440.             err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref));
  4441.         }
  4442.         if (err == noErr) {
  4443.             *active = (atpfPref.ddpPrefs.fLoadType != 0);
  4444.         }
  4445.  
  4446.         err2 = MNSCloseDatabase(&ref, false);
  4447.         if (err == noErr) {
  4448.             err = err2;
  4449.         }
  4450.     }
  4451.     return err;
  4452. }
  4453.  
  4454. static OSStatus SetAppleTalkActiveDatabase(Boolean active)
  4455.     // Implementation of NSHSetAppleTalkActive which uses the Network Setup
  4456.     // database.  See NSHSetAppleTalkActive's comment in header
  4457.     // file for interface specification.
  4458. {
  4459.     OSStatus err;
  4460.     OSStatus err2;
  4461.     MNSDatabaseRef ref;
  4462.     CfgEntityRef currentAppleTalkEntity;
  4463.     OTCfgATalkGeneral atpfPref;
  4464.     Boolean changeNeeded;
  4465.     UInt8 newValue;
  4466.     
  4467.     err = MNSOpenDatabase(&ref, true);
  4468.     if (err == noErr) {
  4469.         changeNeeded = true;
  4470.         
  4471.         err = FindCurrentConnection(&ref, kOTCfgTypeAppleTalk, ¤tAppleTalkEntity);
  4472.         if (err == noErr) {
  4473.             err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref));
  4474.         }
  4475.         if (err == noErr) {
  4476.             if (active) {
  4477.                 newValue = kOTCfgATalkActive;
  4478.             } else {
  4479.                 newValue = kOTCfgATalkInactive;
  4480.             }
  4481.             changeNeeded = (newValue != atpfPref.ddpPrefs.fLoadType);
  4482.             if (changeNeeded) {
  4483.                 atpfPref.ddpPrefs.fLoadType = newValue;
  4484.                 err = MNSSetPref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref));
  4485.             }        
  4486.         }
  4487.  
  4488.         err2 = MNSCloseDatabase(&ref, (err == noErr) && changeNeeded );
  4489.         if (err == noErr) {
  4490.             err = err2;
  4491.         }
  4492.     }
  4493.     return err;
  4494. }
  4495.  
  4496. /////////////////////////////////////////////////////////////////
  4497. #pragma mark ----- AppleTalk On/Off using File -----
  4498.  
  4499. static OSStatus IsAppleTalkActiveFile(Boolean *active)
  4500.     // Implementation of NSHIsAppleTalkActive which uses the legacy
  4501.     // preference files.  See NSHIsAppleTalkActive's comment in header
  4502.     // file for interface specification.
  4503. {
  4504.     OSStatus err;
  4505.     OSStatus err2;
  4506.     SInt16 oldResFile;
  4507.     SInt16 refNum;
  4508.     Handle atpfResource;
  4509.     SInt16 config;
  4510.     
  4511.     err = OpenNetworkPrefFile(kOTCfgTypeAppleTalk, fsRdPerm, &oldResFile, &refNum);
  4512.     if (err == noErr) {
  4513.         err = GetCurrentConfigFromPrefFile(&config);
  4514.  
  4515.         if (err == noErr) {
  4516.             atpfResource = Get1Resource(kOTCfgATalkGeneralPref, config);
  4517.             err = CheckResError(atpfResource);
  4518.         }
  4519.         if (err == noErr && GetHandleSize(atpfResource) != sizeof(OTCfgATalkGeneral)) {
  4520.             err = -1;
  4521.         }
  4522.         if (err == noErr) {
  4523.             *active = (**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType != 0;
  4524.         }
  4525.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  4526.         if (err == noErr) {
  4527.             err = err2;
  4528.         }
  4529.     }
  4530.     
  4531.     return err;
  4532. }
  4533.  
  4534. static OSStatus SetAppleTalkActiveFile(Boolean active)
  4535.     // Implementation of NSHSetAppleTalkActive which uses the legacy
  4536.     // preference files.  See NSHSetAppleTalkActive's comment in header
  4537.     // file for interface specification.
  4538. {
  4539.     OSStatus err;
  4540.     OSStatus err2;
  4541.     SInt16 refNum;
  4542.     SInt16 oldResFile;
  4543.     Handle atpfResource;
  4544.     SInt16 config;
  4545.     Boolean changeNeeded;
  4546.     UInt8 newValue;
  4547.     
  4548.     err = OpenNetworkPrefFile(kOTCfgTypeAppleTalk, fsRdWrPerm, &oldResFile, &refNum);
  4549.     if (err == noErr) {
  4550.         err = GetCurrentConfigFromPrefFile(&config);
  4551.         
  4552.         if (err == noErr) {
  4553.             atpfResource = Get1Resource(kOTCfgATalkGeneralPref, config);
  4554.             err = CheckResError(atpfResource);
  4555.         }
  4556.         if (err == noErr && GetHandleSize(atpfResource) != sizeof(OTCfgATalkGeneral)) {
  4557.             err = -1;
  4558.         }
  4559.         if (err == noErr) {
  4560.             if (active) {
  4561.                 newValue = kOTCfgATalkActive;
  4562.             } else {
  4563.                 newValue = kOTCfgATalkInactive;
  4564.             }
  4565.             changeNeeded = (newValue != (**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType);
  4566.             if (changeNeeded) {
  4567.                 (**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType = newValue;
  4568.                 ChangedResource(atpfResource);
  4569.                 err = ResError();
  4570.                 if (err == noErr) {
  4571.                     UpdateResFile(refNum);
  4572.                     err = ResError();
  4573.                 }
  4574.                 if (err == noErr) {
  4575.                     err = CommitChangesToPrefFile(kOTCfgTypeAppleTalk, refNum, config);
  4576.                 }
  4577.             }
  4578.         }
  4579.         
  4580.         err2 = CloseNetworkPrefFile(oldResFile, refNum);
  4581.         if (err == noErr) {
  4582.             err = err2;
  4583.         }
  4584.     }
  4585.  
  4586.     return err;
  4587. }
  4588.  
  4589. /////////////////////////////////////////////////////////////////
  4590. #pragma mark ----- AppleTalk On/Off Abtraction -----
  4591.  
  4592. extern pascal OSStatus NSHIsAppleTalkActive(Boolean *active)
  4593.     // See comments in interface part.
  4594. {
  4595.     OSStatus err;
  4596.     
  4597.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  4598.         #if TARGET_RT_MAC_CFM
  4599.             err = IsAppleTalkActiveDatabase(active);
  4600.         #else
  4601.             // Network Setup has no Mixed Mode glue.  When running
  4602.             // code on a PowerPC with Network Setup available, you
  4603.             // should either compile your code as Fat or, if that's
  4604.             // infeasible, write your own Mixed Mode glue.
  4605.             return -5;
  4606.         #endif
  4607.     } else {
  4608.         err = IsAppleTalkActiveFile(active);
  4609.     }
  4610.     return err;
  4611. }
  4612.  
  4613. extern pascal OSStatus HSHSetAppleTalkActive(Boolean active)
  4614.     // See comments in interface part.
  4615. {
  4616.     OSStatus err;
  4617.     
  4618.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  4619.         #if TARGET_RT_MAC_CFM
  4620.             err = SetAppleTalkActiveDatabase(active);
  4621.         #else
  4622.             // Network Setup has no Mixed Mode glue.  When running
  4623.             // code on a PowerPC with Network Setup available, you
  4624.             // should either compile your code as Fat or, if that's
  4625.             // infeasible, write your own Mixed Mode glue.
  4626.             return -5;
  4627.         #endif
  4628.     } else {
  4629.         err = SetAppleTalkActiveFile(active);
  4630.     }
  4631.     return err;
  4632. }
  4633.  
  4634. /////////////////////////////////////////////////////////////////
  4635. #pragma mark ----- Remote Access Password Encode -----
  4636.  
  4637. static OSStatus EncodeRemotePasswordNetworkSetup(
  4638.                                 ConstStr255Param userName,
  4639.                                 ConstStr255Param password,
  4640.                                 Str255 encodedPassword)
  4641.     // Implementation of NSHEncodeRemotePassword which uses the Network Setup
  4642.     // routines added in Mac OS 9.0.  See NSHEncodeRemotePassword's comment in
  4643.     // header file for interface specification.
  4644. {
  4645.     MoreBlockZero(encodedPassword, sizeof(Str255));
  4646.     BlockMoveData(password + 1, encodedPassword, password[0]);
  4647.     
  4648.     (void) OTCfgEncrypt( (UInt8 *) userName, encodedPassword, sizeof(Str255));
  4649.     
  4650.     return noErr;
  4651. }
  4652.  
  4653. enum {
  4654.     _RemoteAccess = 0xAA5B
  4655. };
  4656.  
  4657. static OSStatus EncodeRemotePasswordARA(
  4658.                                 ConstStr255Param userName,
  4659.                                 ConstStr255Param password,
  4660.                                 Str255 encodedPassword)
  4661.     // Implementation of NSHEncodeRemotePassword which uses the ARA 2.x
  4662.     // API (which is also emulated by ARA 3.x).  See NSHEncodeRemotePassword's
  4663.     // comment in header file for interface specification.
  4664. {
  4665.     OSStatus err;
  4666.     TRemoteAccessPasswordMunger pb;
  4667.     UInt8 chunkOfPassword[9];
  4668.     ByteCount offset;
  4669.  
  4670.     // Only call PBRemoteAccess if it's implemented!
  4671.     
  4672.     err = unimpErr;
  4673.     if ( GetToolboxTrapAddress(_RemoteAccess) != GetToolboxTrapAddress(_Unimplemented) ) {
  4674.         // Zero the encoded password buffer and copy the 
  4675.         // password (sans length byte) into it.
  4676.         
  4677.         OTMemzero(encodedPassword, sizeof(Str255));
  4678.         BlockMoveData(&password[1], encodedPassword, password[0]);
  4679.         
  4680.         // Now walk through the password in chunks, copying
  4681.         // a chunk into the chunkOfPassword buffer, encrypting
  4682.         // that buffer with the ARA API, and then copying the
  4683.         // buffer back out to the encrypted password.
  4684.         
  4685.         // Note that the for loop increments offset by 8 each time.
  4686.  
  4687.         for (offset = 0; offset < 256 ; offset += 8) {
  4688.         
  4689.             // Copy a chunk of the password into chunkOfPassword.
  4690.             
  4691.             BlockMoveData(encodedPassword + offset, &chunkOfPassword[1], 8);
  4692.             chunkOfPassword[0] = 8;
  4693.  
  4694.             // Setup the parameter block for a remote access API call.
  4695.  
  4696.             pb.csCode        = RAM_EXTENDED_CALL;
  4697.             pb.resultStrPtr = nil;
  4698.             pb.extendedType = (char*) REMOTEACCESSNAME;
  4699.             pb.extendedCode = CmdRemoteAccess_PassWordMunger;
  4700.             pb.userNamePtr  = (UInt8 *) userName;
  4701.             pb.passWordPtr  = chunkOfPassword;
  4702.             pb.reserved     = 0;
  4703.  
  4704.             err = MorePBRemoteAccess((TPRemoteAccessParamBlock) &pb, false);
  4705.             if (err == noErr) {
  4706.                 err = pb.ioResult;
  4707.             }
  4708.             if (err == noErr) {
  4709.  
  4710.                 // Copy the encrypted chunk of password back out into
  4711.                 // encodedPassword.
  4712.                 
  4713.                 BlockMoveData(&chunkOfPassword[1], encodedPassword + offset, 8);
  4714.             } else {
  4715.                 break;
  4716.             }
  4717.         }
  4718.  
  4719.         // Tidy up encoded password.  Move the encrypted data up
  4720.         // one byte and insert a zero length byte.  Don't ask me
  4721.         // why this is required, I didn't invent this scheme (-:
  4722.  
  4723.         BlockMoveData(encodedPassword, encodedPassword + 1, sizeof(Str255) - 1);
  4724.         encodedPassword[0] = 0;
  4725.     }
  4726.     
  4727.     return err;
  4728. }
  4729.  
  4730. extern pascal OSStatus NSHEncodeRemotePassword(
  4731.                                 ConstStr255Param userName,
  4732.                                 ConstStr255Param password,
  4733.                                 Str255 encodedPassword)
  4734.     // See comments in interface part.
  4735. {
  4736.     OSStatus err;
  4737.     
  4738.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  4739.         #if TARGET_RT_MAC_CFM
  4740.             if ( (void *) OTCfgEncrypt != (void *) kUnresolvedCFragSymbolAddress ) {
  4741.                 err = EncodeRemotePasswordNetworkSetup(userName, password, encodedPassword);
  4742.             } else
  4743.         #endif
  4744.             {
  4745.                 err = EncodeRemotePasswordARA(userName, password, encodedPassword);
  4746.             }
  4747.     } else {
  4748.         err = EncodeRemotePasswordARA(userName, password, encodedPassword);
  4749.     }
  4750.     return err;
  4751. }
  4752.  
  4753. /////////////////////////////////////////////////////////////////
  4754.